1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
8 #include "common/ceph_crypto.h"
9 #include "common/split.h"
10 #include "common/Formatter.h"
11 #include "common/utf8.h"
12 #include "common/ceph_json.h"
13 #include "common/safe_io.h"
14 #include "common/errno.h"
15 #include "auth/Crypto.h"
16 #include <boost/algorithm/string.hpp>
17 #include <boost/algorithm/string/replace.hpp>
18 #include <boost/utility/string_view.hpp>
19 #include <boost/tokenizer.hpp>
21 #include <liboath/oath.h>
24 #include "rgw_rest_s3.h"
25 #include "rgw_rest_s3website.h"
26 #include "rgw_rest_pubsub.h"
27 #include "rgw_auth_s3.h"
29 #include "rgw_policy_s3.h"
32 #include "rgw_cors_s3.h"
33 #include "rgw_tag_s3.h"
35 #include "rgw_client_io.h"
37 #include "rgw_keystone.h"
38 #include "rgw_auth_keystone.h"
39 #include "rgw_auth_registry.h"
41 #include "rgw_es_query.h"
43 #include <typeinfo> // for 'typeid'
46 #include "rgw_token.h"
47 #include "rgw_rest_role.h"
48 #include "rgw_crypt.h"
49 #include "rgw_crypt_sanitize.h"
50 #include "rgw_rest_user_policy.h"
52 #include "rgw_bucket_sync.h"
54 #include "services/svc_zone.h"
55 #include "services/svc_cls.h"
57 #include "include/ceph_assert.h"
59 #include "rgw_rest_sts.h"
60 #include "rgw_rest_iam.h"
63 #define dout_context g_ceph_context
64 #define dout_subsys ceph_subsys_rgw
67 using namespace ceph::crypto
;
71 void list_all_buckets_start(struct req_state
*s
)
73 s
->formatter
->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3
);
76 void list_all_buckets_end(struct req_state
*s
)
78 s
->formatter
->close_section();
81 void dump_bucket(struct req_state
*s
, rgw::sal::RGWBucket
& obj
)
83 s
->formatter
->open_object_section("Bucket");
84 s
->formatter
->dump_string("Name", obj
.get_name());
85 dump_time(s
, "CreationDate", &obj
.get_creation_time());
86 s
->formatter
->close_section();
89 void rgw_get_errno_s3(rgw_http_error
*e
, int err_no
)
91 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
);
93 if (r
!= rgw_http_s3_errors
.end()) {
94 e
->http_ret
= r
->second
.first
;
95 e
->s3_code
= r
->second
.second
;
98 e
->s3_code
= "UnknownError";
102 static inline std::string
get_s3_expiration_header(
104 const ceph::real_time
& mtime
)
106 return rgw::lc::s3_expiration_header(
107 s
, s
->object
, s
->tagset
, mtime
, s
->bucket_attrs
);
110 static inline bool get_s3_multipart_abort_header(
111 struct req_state
* s
, const ceph::real_time
& mtime
,
112 ceph::real_time
& date
, std::string
& rule_id
)
114 return rgw::lc::s3_multipart_abort_header(
115 s
, s
->object
, mtime
, s
->bucket_attrs
, date
, rule_id
);
118 struct response_attr_param
{
120 const char *http_attr
;
123 static struct response_attr_param resp_attr_params
[] = {
124 {"response-content-type", "Content-Type"},
125 {"response-content-language", "Content-Language"},
126 {"response-expires", "Expires"},
127 {"response-cache-control", "Cache-Control"},
128 {"response-content-disposition", "Content-Disposition"},
129 {"response-content-encoding", "Content-Encoding"},
133 int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist
& bl
, off_t bl_ofs
, off_t bl_len
) {
134 map
<string
, bufferlist
>::iterator iter
;
135 iter
= attrs
.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION
);
136 if (iter
!= attrs
.end()) {
137 bufferlist
&bl
= iter
->second
;
138 s
->redirect
= bl
.c_str();
139 s
->err
.http_ret
= 301;
140 ldpp_dout(this, 20) << __CEPH_ASSERT_FUNCTION
<< " redirecting per x-amz-website-redirect-location=" << s
->redirect
<< dendl
;
141 op_ret
= -ERR_WEBSITE_REDIRECT
;
142 set_req_state_err(s
, op_ret
);
144 dump_content_length(s
, 0);
145 dump_redirect(s
, s
->redirect
);
149 return RGWGetObj_ObjStore_S3::send_response_data(bl
, bl_ofs
, bl_len
);
153 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
155 return RGWGetObj_ObjStore_S3::send_response_data_error();
158 int RGWGetObj_ObjStore_S3::get_params()
160 // for multisite sync requests, only read the slo manifest itself, rather than
161 // all of the data from its parts. the parts will sync as separate objects
162 skip_manifest
= s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"sync-manifest");
164 // multisite sync requests should fetch encrypted data, along with the
165 // attributes needed to support decryption on the other zone
166 if (s
->system_request
) {
167 skip_decrypt
= s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"skip-decrypt");
170 return RGWGetObj_ObjStore::get_params();
173 int RGWGetObj_ObjStore_S3::send_response_data_error()
176 return send_response_data(bl
, 0 , 0);
180 int decode_attr_bl_single_value(map
<string
, bufferlist
>& attrs
, const char *attr_name
, T
*result
, T def_val
)
182 map
<string
, bufferlist
>::iterator iter
= attrs
.find(attr_name
);
183 if (iter
== attrs
.end()) {
187 bufferlist
& bl
= iter
->second
;
188 if (bl
.length() == 0) {
192 auto bliter
= bl
.cbegin();
194 decode(*result
, bliter
);
195 } catch (buffer::error
& err
) {
201 inline bool str_has_cntrl(const std::string s
) {
202 return std::any_of(s
.begin(), s
.end(), ::iscntrl
);
205 inline bool str_has_cntrl(const char* s
) {
207 return str_has_cntrl(_s
);
210 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist
& bl
, off_t bl_ofs
,
213 const char *content_type
= NULL
;
214 string content_type_str
;
215 map
<string
, string
> response_attrs
;
216 map
<string
, string
>::iterator riter
;
217 bufferlist metadata_bl
;
219 string expires
= get_s3_expiration_header(s
, lastmod
);
224 if (custom_http_ret
) {
225 set_req_state_err(s
, 0);
226 dump_errno(s
, custom_http_ret
);
228 set_req_state_err(s
, (partial_content
&& !op_ret
) ? STATUS_PARTIAL_CONTENT
237 dump_range(s
, start
, end
, s
->obj_size
);
239 if (s
->system_request
&&
240 s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"prepend-metadata")) {
242 dump_header(s
, "Rgwx-Object-Size", (long long)total_len
);
246 * in this case, we're not returning the object's content, only the prepended
252 /* JSON encode object metadata */
254 jf
.open_object_section("obj_metadata");
255 encode_json("attrs", attrs
, &jf
);
257 encode_json("mtime", ut
, &jf
);
261 metadata_bl
.append(ss
.str());
262 dump_header(s
, "Rgwx-Embedded-Metadata-Len", metadata_bl
.length());
263 total_len
+= metadata_bl
.length();
266 if (s
->system_request
&& !real_clock::is_zero(lastmod
)) {
267 /* we end up dumping mtime in two different methods, a bit redundant */
268 dump_epoch_header(s
, "Rgwx-Mtime", lastmod
);
270 int r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_PG_VER
, &pg_ver
, (uint64_t)0);
272 ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
274 dump_header(s
, "Rgwx-Obj-PG-Ver", pg_ver
);
276 uint32_t source_zone_short_id
= 0;
277 r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_SOURCE_ZONE
, &source_zone_short_id
, (uint32_t)0);
279 ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
281 if (source_zone_short_id
!= 0) {
282 dump_header(s
, "Rgwx-Source-Zone-Short-Id", source_zone_short_id
);
286 for (auto &it
: crypt_http_responses
)
287 dump_header(s
, it
.first
, it
.second
);
289 dump_content_length(s
, total_len
);
290 dump_last_modified(s
, lastmod
);
291 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
292 dump_header_if_nonempty(s
, "x-amz-expiration", expires
);
294 if (attrs
.find(RGW_ATTR_APPEND_PART_NUM
) != attrs
.end()) {
295 dump_header(s
, "x-rgw-object-type", "Appendable");
296 dump_header(s
, "x-rgw-next-append-position", s
->obj_size
);
298 dump_header(s
, "x-rgw-object-type", "Normal");
302 if (! lo_etag
.empty()) {
303 /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
304 * legit to perform GET on them through S3 API. In such situation,
305 * a client should receive the composited content with corresponding
307 dump_etag(s
, lo_etag
);
309 auto iter
= attrs
.find(RGW_ATTR_ETAG
);
310 if (iter
!= attrs
.end()) {
311 dump_etag(s
, iter
->second
.to_str());
315 for (struct response_attr_param
*p
= resp_attr_params
; p
->param
; p
++) {
317 string val
= s
->info
.args
.get(p
->param
, &exists
);
319 /* reject unauthenticated response header manipulation, see
320 * https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html */
321 if (s
->auth
.identity
->is_anonymous()) {
322 return -ERR_INVALID_REQUEST
;
324 /* HTTP specification says no control characters should be present in
325 * header values: https://tools.ietf.org/html/rfc7230#section-3.2
326 * field-vchar = VCHAR / obs-text
328 * Failure to validate this permits a CRLF injection in HTTP headers,
329 * whereas S3 GetObject only permits specific headers.
331 if(str_has_cntrl(val
)) {
332 /* TODO: return a more distinct error in future;
333 * stating what the problem is */
334 return -ERR_INVALID_REQUEST
;
337 if (strcmp(p
->param
, "response-content-type") != 0) {
338 response_attrs
[p
->http_attr
] = val
;
340 content_type_str
= val
;
341 content_type
= content_type_str
.c_str();
346 for (auto iter
= attrs
.begin(); iter
!= attrs
.end(); ++iter
) {
347 const char *name
= iter
->first
.c_str();
348 map
<string
, string
>::iterator aiter
= rgw_to_http_attrs
.find(name
);
349 if (aiter
!= rgw_to_http_attrs
.end()) {
350 if (response_attrs
.count(aiter
->second
) == 0) {
351 /* Was not already overridden by a response param. */
353 size_t len
= iter
->second
.length();
354 string
s(iter
->second
.c_str(), len
);
355 while (len
&& !s
[len
- 1]) {
359 response_attrs
[aiter
->second
] = s
;
361 } else if (iter
->first
.compare(RGW_ATTR_CONTENT_TYPE
) == 0) {
362 /* Special handling for content_type. */
364 content_type_str
= rgw_bl_str(iter
->second
);
365 content_type
= content_type_str
.c_str();
367 } else if (strcmp(name
, RGW_ATTR_SLO_UINDICATOR
) == 0) {
368 // this attr has an extra length prefix from encode() in prior versions
369 dump_header(s
, "X-Object-Meta-Static-Large-Object", "True");
370 } else if (strncmp(name
, RGW_ATTR_META_PREFIX
,
371 sizeof(RGW_ATTR_META_PREFIX
)-1) == 0) {
372 /* User custom metadata. */
373 name
+= sizeof(RGW_ATTR_PREFIX
) - 1;
374 dump_header(s
, name
, iter
->second
);
375 } else if (iter
->first
.compare(RGW_ATTR_TAGS
) == 0) {
378 auto it
= iter
->second
.cbegin();
380 } catch (buffer::error
&err
) {
381 ldpp_dout(this,0) << "Error caught buffer::error couldn't decode TagSet " << dendl
;
383 dump_header(s
, RGW_AMZ_TAG_COUNT
, obj_tags
.count());
384 } else if (iter
->first
.compare(RGW_ATTR_OBJECT_RETENTION
) == 0 && get_retention
){
385 RGWObjectRetention retention
;
387 decode(retention
, iter
->second
);
388 dump_header(s
, "x-amz-object-lock-mode", retention
.get_mode());
389 dump_time_header(s
, "x-amz-object-lock-retain-until-date", retention
.get_retain_until_date());
390 } catch (buffer::error
& err
) {
391 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl
;
393 } else if (iter
->first
.compare(RGW_ATTR_OBJECT_LEGAL_HOLD
) == 0 && get_legal_hold
) {
394 RGWObjectLegalHold legal_hold
;
396 decode(legal_hold
, iter
->second
);
397 dump_header(s
, "x-amz-object-lock-legal-hold",legal_hold
.get_status());
398 } catch (buffer::error
& err
) {
399 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl
;
406 for (riter
= response_attrs
.begin(); riter
!= response_attrs
.end();
408 dump_header(s
, riter
->first
, riter
->second
);
411 if (op_ret
== -ERR_NOT_MODIFIED
) {
415 content_type
= "binary/octet-stream";
417 end_header(s
, this, content_type
);
420 if (metadata_bl
.length()) {
421 dump_body(s
, metadata_bl
);
426 if (get_data
&& !op_ret
) {
427 int r
= dump_body(s
, bl
.c_str() + bl_ofs
, bl_len
);
435 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr
<RGWGetObj_Filter
> *filter
, RGWGetObj_Filter
* cb
, bufferlist
* manifest_bl
)
437 if (skip_decrypt
) { // bypass decryption for multisite sync requests
442 std::unique_ptr
<BlockCrypt
> block_crypt
;
443 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses
);
445 if (block_crypt
!= nullptr) {
446 auto f
= std::make_unique
<RGWGetObj_BlockDecrypt
>(s
->cct
, cb
, std::move(block_crypt
));
447 if (manifest_bl
!= nullptr) {
448 res
= f
->read_manifest(*manifest_bl
);
450 *filter
= std::move(f
);
457 int RGWGetObj_ObjStore_S3::verify_requester(const rgw::auth::StrategyRegistry
& auth_registry
)
460 ret
= RGWOp::verify_requester(auth_registry
);
461 if(!s
->user
->get_caps().check_cap("amz-cache", RGW_CAP_READ
) && !ret
&& s
->info
.env
->exists("HTTP_X_AMZ_CACHE"))
462 ret
= override_range_hdr(auth_registry
);
465 int RGWGetObj_ObjStore_S3::override_range_hdr(const rgw::auth::StrategyRegistry
& auth_registry
)
468 ldpp_dout(this, 10) << "cache override headers" << dendl
;
469 RGWEnv
* rgw_env
= const_cast<RGWEnv
*>(s
->info
.env
);
470 const char* backup_range
= rgw_env
->get("HTTP_RANGE");
471 const char hdrs_split
[2] = {(char)178,'\0'};
472 const char kv_split
[2] = {(char)177,'\0'};
473 const char* cache_hdr
= rgw_env
->get("HTTP_X_AMZ_CACHE");
474 for (std::string_view hdr
: ceph::split(cache_hdr
, hdrs_split
)) {
475 auto kv
= ceph::split(hdr
, kv_split
);
477 if (std::distance(k
, kv
.end()) != 2) {
480 auto v
= std::next(k
);
481 std::string key
= "HTTP_";
483 boost::replace_all(key
, "-", "_");
484 rgw_env
->set(std::move(key
), std::string(*v
));
485 ldpp_dout(this, 10) << "after splitting cache kv key: " << key
<< " " << rgw_env
->get(key
.c_str()) << dendl
;
487 ret
= RGWOp::verify_requester(auth_registry
);
488 if(!ret
&& backup_range
) {
489 rgw_env
->set("HTTP_RANGE",backup_range
);
491 rgw_env
->remove("HTTP_RANGE");
497 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist
& bl
)
500 end_header(s
, this, "application/xml");
503 s
->formatter
->open_object_section_in_ns("Tagging", XMLNS_AWS_S3
);
504 s
->formatter
->open_object_section("TagSet");
506 RGWObjTagSet_S3 tagset
;
507 auto iter
= bl
.cbegin();
510 } catch (buffer::error
& err
) {
511 ldpp_dout(this,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl
;
515 tagset
.dump_xml(s
->formatter
);
517 s
->formatter
->close_section();
518 s
->formatter
->close_section();
519 rgw_flush_formatter_and_reset(s
, s
->formatter
);
523 int RGWPutObjTags_ObjStore_S3::get_params()
531 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
535 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
540 if (!parser
.parse(data
.c_str(), data
.length(), 1)) {
541 return -ERR_MALFORMED_XML
;
544 RGWObjTagging_S3 tagging
;
547 RGWXMLDecoder::decode_xml("Tagging", tagging
, &parser
);
548 } catch (RGWXMLDecoder::err
& err
) {
549 ldpp_dout(this, 5) << "Malformed tagging request: " << err
<< dendl
;
550 return -ERR_MALFORMED_XML
;
554 r
= tagging
.rebuild(obj_tags
);
558 obj_tags
.encode(tags_bl
);
559 ldpp_dout(this, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
564 void RGWPutObjTags_ObjStore_S3::send_response()
567 set_req_state_err(s
, op_ret
);
569 end_header(s
, this, "application/xml");
574 void RGWDeleteObjTags_ObjStore_S3::send_response()
580 r
= STATUS_NO_CONTENT
;
582 set_req_state_err(s
, r
);
587 void RGWGetBucketTags_ObjStore_S3::send_response_data(bufferlist
& bl
)
590 set_req_state_err(s
, op_ret
);
592 end_header(s
, this, "application/xml");
596 s
->formatter
->open_object_section_in_ns("Tagging", XMLNS_AWS_S3
);
597 s
->formatter
->open_object_section("TagSet");
599 RGWObjTagSet_S3 tagset
;
600 auto iter
= bl
.cbegin();
603 } catch (buffer::error
& err
) {
604 ldout(s
->cct
,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl
;
608 tagset
.dump_xml(s
->formatter
);
610 s
->formatter
->close_section();
611 s
->formatter
->close_section();
612 rgw_flush_formatter_and_reset(s
, s
->formatter
);
616 int RGWPutBucketTags_ObjStore_S3::get_params()
624 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
628 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
633 if (!parser
.parse(data
.c_str(), data
.length(), 1)) {
634 return -ERR_MALFORMED_XML
;
637 RGWObjTagging_S3 tagging
;
639 RGWXMLDecoder::decode_xml("Tagging", tagging
, &parser
);
640 } catch (RGWXMLDecoder::err
& err
) {
642 ldout(s
->cct
, 5) << "Malformed tagging request: " << err
<< dendl
;
643 return -ERR_MALFORMED_XML
;
646 RGWObjTags
obj_tags(50); // A tag set can contain as many as 50 tags, or it can be empty.
647 r
= tagging
.rebuild(obj_tags
);
651 obj_tags
.encode(tags_bl
);
652 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
654 // forward bucket tags requests to meta master zone
655 if (!store
->svc()->zone
->is_meta_master()) {
656 /* only need to keep this data around if we're not meta master */
657 in_data
= std::move(data
);
663 void RGWPutBucketTags_ObjStore_S3::send_response()
666 set_req_state_err(s
, op_ret
);
668 end_header(s
, this, "application/xml");
672 void RGWDeleteBucketTags_ObjStore_S3::send_response()
675 set_req_state_err(s
, op_ret
);
677 end_header(s
, this, "application/xml");
683 bool is_valid_status(const string
& s
) {
684 return (s
== "Enabled" ||
688 static string enabled_group_id
= "s3-bucket-replication:enabled";
689 static string disabled_group_id
= "s3-bucket-replication:disabled";
691 struct ReplicationConfiguration
{
695 struct DeleteMarkerReplication
{
698 void decode_xml(XMLObj
*obj
) {
699 RGWXMLDecoder::decode_xml("Status", status
, obj
);
702 void dump_xml(Formatter
*f
) const {
703 encode_xml("Status", status
, f
);
706 bool is_valid(CephContext
*cct
) const {
707 bool result
= is_valid_status(status
);
709 ldout(cct
, 5) << "NOTICE: bad status provided in DeleteMarkerReplication element (status=" << status
<< ")" << dendl
;
715 struct Source
{ /* rgw extension */
716 std::vector
<string
> zone_names
;
718 void decode_xml(XMLObj
*obj
) {
719 RGWXMLDecoder::decode_xml("Zone", zone_names
, obj
);
722 void dump_xml(Formatter
*f
) const {
723 encode_xml("Zone", zone_names
, f
);
728 struct AccessControlTranslation
{
731 void decode_xml(XMLObj
*obj
) {
732 RGWXMLDecoder::decode_xml("Owner", owner
, obj
);
734 void dump_xml(Formatter
*f
) const {
735 encode_xml("Owner", owner
, f
);
739 std::optional
<AccessControlTranslation
> acl_translation
;
740 std::optional
<string
> account
;
742 std::optional
<string
> storage_class
;
743 std::vector
<string
> zone_names
;
745 void decode_xml(XMLObj
*obj
) {
746 RGWXMLDecoder::decode_xml("AccessControlTranslation", acl_translation
, obj
);
747 RGWXMLDecoder::decode_xml("Account", account
, obj
);
748 if (account
&& account
->empty()) {
751 RGWXMLDecoder::decode_xml("Bucket", bucket
, obj
);
752 RGWXMLDecoder::decode_xml("StorageClass", storage_class
, obj
);
753 if (storage_class
&& storage_class
->empty()) {
754 storage_class
.reset();
756 RGWXMLDecoder::decode_xml("Zone", zone_names
, obj
); /* rgw extension */
759 void dump_xml(Formatter
*f
) const {
760 encode_xml("AccessControlTranslation", acl_translation
, f
);
761 encode_xml("Account", account
, f
);
762 encode_xml("Bucket", bucket
, f
);
763 encode_xml("StorageClass", storage_class
, f
);
764 encode_xml("Zone", zone_names
, f
);
774 return key
.empty() && value
.empty();
777 void decode_xml(XMLObj
*obj
) {
778 RGWXMLDecoder::decode_xml("Key", key
, obj
);
779 RGWXMLDecoder::decode_xml("Value", value
, obj
);
782 void dump_xml(Formatter
*f
) const {
783 encode_xml("Key", key
, f
);
784 encode_xml("Value", value
, f
);
789 std::optional
<string
> prefix
;
790 std::vector
<Tag
> tags
;
797 void decode_xml(XMLObj
*obj
) {
798 std::vector
<Tag
> _tags
;
799 RGWXMLDecoder::decode_xml("Prefix", prefix
, obj
);
800 if (prefix
&& prefix
->empty()) {
803 RGWXMLDecoder::decode_xml("Tag", _tags
, obj
);
804 for (auto& t
: _tags
) {
806 tags
.push_back(std::move(t
));
811 void dump_xml(Formatter
*f
) const {
812 encode_xml("Prefix", prefix
, f
);
813 encode_xml("Tag", tags
, f
);
817 std::optional
<string
> prefix
;
818 std::optional
<Tag
> tag
;
819 std::optional
<AndElements
> and_elements
;
822 return (!prefix
&& !tag
&& !and_elements
);
825 void decode_xml(XMLObj
*obj
) {
826 RGWXMLDecoder::decode_xml("Prefix", prefix
, obj
);
827 if (prefix
&& prefix
->empty()) {
830 RGWXMLDecoder::decode_xml("Tag", tag
, obj
);
831 if (tag
&& tag
->empty()) {
834 RGWXMLDecoder::decode_xml("And", and_elements
, obj
);
835 if (and_elements
&& and_elements
->empty()) {
836 and_elements
.reset();
840 void dump_xml(Formatter
*f
) const {
841 encode_xml("Prefix", prefix
, f
);
842 encode_xml("Tag", tag
, f
);
843 encode_xml("And", and_elements
, f
);
846 bool is_valid(CephContext
*cct
) const {
848 ldout(cct
, 5) << "NOTICE: both tag and prefix were provided in replication filter rule" << dendl
;
853 if (prefix
&& and_elements
->prefix
) {
854 ldout(cct
, 5) << "NOTICE: too many prefixes were provided in re" << dendl
;
861 int to_sync_pipe_filter(CephContext
*cct
,
862 rgw_sync_pipe_filter
*f
) const {
863 if (!is_valid(cct
)) {
870 f
->tags
.insert(rgw_sync_pipe_filter_tag(tag
->key
, tag
->value
));
874 if (and_elements
->prefix
) {
875 f
->prefix
= *and_elements
->prefix
;
877 for (auto& t
: and_elements
->tags
) {
878 f
->tags
.insert(rgw_sync_pipe_filter_tag(t
.key
, t
.value
));
884 void from_sync_pipe_filter(const rgw_sync_pipe_filter
& f
) {
885 if (f
.prefix
&& f
.tags
.empty()) {
890 and_elements
.emplace();
891 and_elements
->prefix
= f
.prefix
;
892 } else if (f
.tags
.size() == 1) {
893 auto iter
= f
.tags
.begin();
894 if (iter
== f
.tags
.end()) {
895 /* should never happen */
901 tag
->value
= t
.value
;
905 if (f
.tags
.empty()) {
910 and_elements
.emplace();
913 for (auto& t
: f
.tags
) {
914 auto& tag
= and_elements
->tags
.emplace_back();
921 set
<rgw_zone_id
> get_zone_ids_from_names(rgw::sal::RGWRadosStore
*store
,
922 const vector
<string
>& zone_names
) const {
923 set
<rgw_zone_id
> ids
;
925 for (auto& name
: zone_names
) {
927 if (store
->svc()->zone
->find_zone_id_by_name(name
, &id
)) {
928 ids
.insert(std::move(id
));
935 vector
<string
> get_zone_names_from_ids(rgw::sal::RGWRadosStore
*store
,
936 const set
<rgw_zone_id
>& zone_ids
) const {
937 vector
<string
> names
;
939 for (auto& id
: zone_ids
) {
941 if (store
->svc()->zone
->find_zone(id
, &zone
)) {
942 names
.emplace_back(zone
->name
);
949 std::optional
<DeleteMarkerReplication
> delete_marker_replication
;
950 std::optional
<Source
> source
;
951 Destination destination
;
952 std::optional
<Filter
> filter
;
957 void decode_xml(XMLObj
*obj
) {
958 RGWXMLDecoder::decode_xml("DeleteMarkerReplication", delete_marker_replication
, obj
);
959 RGWXMLDecoder::decode_xml("Source", source
, obj
);
960 RGWXMLDecoder::decode_xml("Destination", destination
, obj
);
961 RGWXMLDecoder::decode_xml("ID", id
, obj
);
963 std::optional
<string
> prefix
;
964 RGWXMLDecoder::decode_xml("Prefix", prefix
, obj
);
967 filter
->prefix
= prefix
;
971 RGWXMLDecoder::decode_xml("Filter", filter
, obj
);
973 /* don't want to have filter reset because it might have been initialized
974 * when decoding prefix
976 RGWXMLDecoder::decode_xml("Filter", *filter
, obj
);
979 RGWXMLDecoder::decode_xml("Priority", priority
, obj
);
980 RGWXMLDecoder::decode_xml("Status", status
, obj
);
983 void dump_xml(Formatter
*f
) const {
984 encode_xml("DeleteMarkerReplication", delete_marker_replication
, f
);
985 encode_xml("Source", source
, f
);
986 encode_xml("Destination", destination
, f
);
987 encode_xml("Filter", filter
, f
);
988 encode_xml("ID", id
, f
);
989 encode_xml("Priority", priority
, f
);
990 encode_xml("Status", status
, f
);
993 bool is_valid(CephContext
*cct
) const {
994 if (!is_valid_status(status
)) {
995 ldout(cct
, 5) << "NOTICE: bad status provided in rule (status=" << status
<< ")" << dendl
;
998 if ((filter
&& !filter
->is_valid(cct
)) ||
999 (delete_marker_replication
&& !delete_marker_replication
->is_valid(cct
))) {
1005 int to_sync_policy_pipe(req_state
*s
, rgw::sal::RGWRadosStore
*store
,
1006 rgw_sync_bucket_pipes
*pipe
,
1007 bool *enabled
) const {
1008 if (!is_valid(s
->cct
)) {
1013 pipe
->params
.priority
= priority
;
1015 const auto& user_id
= s
->user
->get_id();
1017 rgw_bucket_key
dest_bk(user_id
.tenant
,
1018 destination
.bucket
);
1020 if (source
&& !source
->zone_names
.empty()) {
1021 pipe
->source
.zones
= get_zone_ids_from_names(store
, source
->zone_names
);
1023 pipe
->source
.set_all_zones(true);
1025 if (!destination
.zone_names
.empty()) {
1026 pipe
->dest
.zones
= get_zone_ids_from_names(store
, destination
.zone_names
);
1028 pipe
->dest
.set_all_zones(true);
1030 pipe
->dest
.bucket
.emplace(dest_bk
);
1033 int r
= filter
->to_sync_pipe_filter(s
->cct
, &pipe
->params
.source
.filter
);
1038 if (destination
.acl_translation
) {
1040 u
.tenant
= user_id
.tenant
;
1041 u
.from_str(destination
.acl_translation
->owner
); /* explicit tenant will override tenant,
1042 otherwise will inherit it from s->user */
1043 pipe
->params
.dest
.acl_translation
.emplace();
1044 pipe
->params
.dest
.acl_translation
->owner
= u
;
1046 pipe
->params
.dest
.storage_class
= destination
.storage_class
;
1048 *enabled
= (status
== "Enabled");
1050 pipe
->params
.mode
= rgw_sync_pipe_params::Mode::MODE_USER
;
1051 pipe
->params
.user
= user_id
.to_str();
1056 void from_sync_policy_pipe(rgw::sal::RGWRadosStore
*store
,
1057 const rgw_sync_bucket_pipes
& pipe
,
1060 status
= (enabled
? "Enabled" : "Disabled");
1061 priority
= pipe
.params
.priority
;
1063 if (pipe
.source
.all_zones
) {
1065 } else if (pipe
.source
.zones
) {
1067 source
->zone_names
= get_zone_names_from_ids(store
, *pipe
.source
.zones
);
1070 if (!pipe
.dest
.all_zones
&&
1072 destination
.zone_names
= get_zone_names_from_ids(store
, *pipe
.dest
.zones
);
1075 if (pipe
.params
.dest
.acl_translation
) {
1076 destination
.acl_translation
.emplace();
1077 destination
.acl_translation
->owner
= pipe
.params
.dest
.acl_translation
->owner
.to_str();
1080 if (pipe
.params
.dest
.storage_class
) {
1081 destination
.storage_class
= *pipe
.params
.dest
.storage_class
;
1084 if (pipe
.dest
.bucket
) {
1085 destination
.bucket
= pipe
.dest
.bucket
->get_key();
1089 filter
->from_sync_pipe_filter(pipe
.params
.source
.filter
);
1091 if (filter
->empty()) {
1097 std::vector
<Rule
> rules
;
1099 void decode_xml(XMLObj
*obj
) {
1100 RGWXMLDecoder::decode_xml("Role", role
, obj
);
1101 RGWXMLDecoder::decode_xml("Rule", rules
, obj
);
1104 void dump_xml(Formatter
*f
) const {
1105 encode_xml("Role", role
, f
);
1106 encode_xml("Rule", rules
, f
);
1109 int to_sync_policy_groups(req_state
*s
, rgw::sal::RGWRadosStore
*store
,
1110 vector
<rgw_sync_policy_group
> *result
) const {
1113 rgw_sync_policy_group
& enabled_group
= (*result
)[0];
1114 rgw_sync_policy_group
& disabled_group
= (*result
)[1];
1116 enabled_group
.id
= enabled_group_id
;
1117 enabled_group
.status
= rgw_sync_policy_group::Status::ENABLED
;
1118 disabled_group
.id
= disabled_group_id
;
1119 disabled_group
.status
= rgw_sync_policy_group::Status::ALLOWED
; /* not enabled, not forbidden */
1121 for (auto& rule
: rules
) {
1122 rgw_sync_bucket_pipes pipe
;
1124 int r
= rule
.to_sync_policy_pipe(s
, store
, &pipe
, &enabled
);
1126 ldout(s
->cct
, 5) << "NOTICE: failed to convert replication configuration into sync policy pipe (rule.id=" << rule
.id
<< "): " << cpp_strerror(-r
) << dendl
;
1131 enabled_group
.pipes
.emplace_back(std::move(pipe
));
1133 disabled_group
.pipes
.emplace_back(std::move(pipe
));
1139 void from_sync_policy_group(rgw::sal::RGWRadosStore
*store
,
1140 const rgw_sync_policy_group
& group
) {
1142 bool enabled
= (group
.status
== rgw_sync_policy_group::Status::ENABLED
);
1144 for (auto& pipe
: group
.pipes
) {
1145 auto& rule
= rules
.emplace_back();
1146 rule
.from_sync_policy_pipe(store
, pipe
, enabled
);
1153 void RGWGetBucketReplication_ObjStore_S3::send_response_data()
1156 set_req_state_err(s
, op_ret
);
1158 end_header(s
, this, "application/xml");
1161 ReplicationConfiguration conf
;
1163 if (s
->bucket_info
.sync_policy
) {
1164 auto policy
= s
->bucket_info
.sync_policy
;
1166 auto iter
= policy
->groups
.find(enabled_group_id
);
1167 if (iter
!= policy
->groups
.end()) {
1168 conf
.from_sync_policy_group(store
, iter
->second
);
1170 iter
= policy
->groups
.find(disabled_group_id
);
1171 if (iter
!= policy
->groups
.end()) {
1172 conf
.from_sync_policy_group(store
, iter
->second
);
1177 s
->formatter
->open_object_section_in_ns("ReplicationConfiguration", XMLNS_AWS_S3
);
1178 conf
.dump_xml(s
->formatter
);
1179 s
->formatter
->close_section();
1180 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1184 int RGWPutBucketReplication_ObjStore_S3::get_params()
1186 RGWXMLParser parser
;
1188 if (!parser
.init()){
1192 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1196 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
1201 if (!parser
.parse(data
.c_str(), data
.length(), 1)) {
1202 return -ERR_MALFORMED_XML
;
1205 ReplicationConfiguration conf
;
1207 RGWXMLDecoder::decode_xml("ReplicationConfiguration", conf
, &parser
);
1208 } catch (RGWXMLDecoder::err
& err
) {
1210 ldout(s
->cct
, 5) << "Malformed tagging request: " << err
<< dendl
;
1211 return -ERR_MALFORMED_XML
;
1214 r
= conf
.to_sync_policy_groups(s
, store
, &sync_policy_groups
);
1219 // forward requests to meta master zone
1220 if (!store
->svc()->zone
->is_meta_master()) {
1221 /* only need to keep this data around if we're not meta master */
1222 in_data
= std::move(data
);
1228 void RGWPutBucketReplication_ObjStore_S3::send_response()
1231 set_req_state_err(s
, op_ret
);
1233 end_header(s
, this, "application/xml");
1237 void RGWDeleteBucketReplication_ObjStore_S3::update_sync_policy(rgw_sync_policy_info
*policy
)
1239 policy
->groups
.erase(enabled_group_id
);
1240 policy
->groups
.erase(disabled_group_id
);
1243 void RGWDeleteBucketReplication_ObjStore_S3::send_response()
1246 set_req_state_err(s
, op_ret
);
1248 end_header(s
, this, "application/xml");
1252 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets
)
1255 set_req_state_err(s
, op_ret
);
1258 // Explicitly use chunked transfer encoding so that we can stream the result
1259 // to the user without having to wait for the full length of it.
1260 end_header(s
, NULL
, "application/xml", CHUNKED_TRANSFER_ENCODING
);
1263 list_all_buckets_start(s
);
1264 dump_owner(s
, s
->user
->get_id(), s
->user
->get_display_name());
1265 s
->formatter
->open_array_section("Buckets");
1270 void RGWListBuckets_ObjStore_S3::send_response_data(rgw::sal::RGWBucketList
& buckets
)
1275 map
<string
, rgw::sal::RGWBucket
*>& m
= buckets
.get_buckets();
1276 map
<string
, rgw::sal::RGWBucket
*>::iterator iter
;
1278 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
1279 rgw::sal::RGWBucket
* obj
= iter
->second
;
1280 dump_bucket(s
, *obj
);
1282 rgw_flush_formatter(s
, s
->formatter
);
1285 void RGWListBuckets_ObjStore_S3::send_response_end()
1288 s
->formatter
->close_section();
1289 list_all_buckets_end(s
);
1290 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1294 int RGWGetUsage_ObjStore_S3::get_params()
1296 start_date
= s
->info
.args
.get("start-date");
1297 end_date
= s
->info
.args
.get("end-date");
1301 static void dump_usage_categories_info(Formatter
*formatter
, const rgw_usage_log_entry
& entry
, map
<string
, bool> *categories
)
1303 formatter
->open_array_section("categories");
1304 map
<string
, rgw_usage_data
>::const_iterator uiter
;
1305 for (uiter
= entry
.usage_map
.begin(); uiter
!= entry
.usage_map
.end(); ++uiter
) {
1306 if (categories
&& !categories
->empty() && !categories
->count(uiter
->first
))
1308 const rgw_usage_data
& usage
= uiter
->second
;
1309 formatter
->open_object_section("Entry");
1310 encode_json("Category", uiter
->first
, formatter
);
1311 encode_json("BytesSent", usage
.bytes_sent
, formatter
);
1312 encode_json("BytesReceived", usage
.bytes_received
, formatter
);
1313 encode_json("Ops", usage
.ops
, formatter
);
1314 encode_json("SuccessfulOps", usage
.successful_ops
, formatter
);
1315 formatter
->close_section(); // Entry
1317 formatter
->close_section(); // Category
1320 static void dump_usage_bucket_info(Formatter
*formatter
, const std::string
& name
, const cls_user_bucket_entry
& entry
)
1322 formatter
->open_object_section("Entry");
1323 encode_json("Bucket", name
, formatter
);
1324 encode_json("Bytes", entry
.size
, formatter
);
1325 encode_json("Bytes_Rounded", entry
.size_rounded
, formatter
);
1326 formatter
->close_section(); // entry
1329 void RGWGetUsage_ObjStore_S3::send_response()
1332 set_req_state_err(s
, op_ret
);
1335 // Explicitly use chunked transfer encoding so that we can stream the result
1336 // to the user without having to wait for the full length of it.
1337 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
1342 Formatter
*formatter
= s
->formatter
;
1344 bool user_section_open
= false;
1346 formatter
->open_object_section("Usage");
1347 if (show_log_entries
) {
1348 formatter
->open_array_section("Entries");
1350 map
<rgw_user_bucket
, rgw_usage_log_entry
>::iterator iter
;
1351 for (iter
= usage
.begin(); iter
!= usage
.end(); ++iter
) {
1352 const rgw_user_bucket
& ub
= iter
->first
;
1353 const rgw_usage_log_entry
& entry
= iter
->second
;
1355 if (show_log_entries
) {
1356 if (ub
.user
.compare(last_owner
) != 0) {
1357 if (user_section_open
) {
1358 formatter
->close_section();
1359 formatter
->close_section();
1361 formatter
->open_object_section("User");
1362 formatter
->dump_string("Owner", ub
.user
);
1363 formatter
->open_array_section("Buckets");
1364 user_section_open
= true;
1365 last_owner
= ub
.user
;
1367 formatter
->open_object_section("Bucket");
1368 formatter
->dump_string("Bucket", ub
.bucket
);
1369 utime_t
ut(entry
.epoch
, 0);
1370 ut
.gmtime(formatter
->dump_stream("Time"));
1371 formatter
->dump_int("Epoch", entry
.epoch
);
1372 dump_usage_categories_info(formatter
, entry
, &categories
);
1373 formatter
->close_section(); // bucket
1376 summary_map
[ub
.user
].aggregate(entry
, &categories
);
1379 if (show_log_entries
) {
1380 if (user_section_open
) {
1381 formatter
->close_section(); // buckets
1382 formatter
->close_section(); //user
1384 formatter
->close_section(); // entries
1388 formatter
->open_array_section("Summary");
1389 map
<string
, rgw_usage_log_entry
>::iterator siter
;
1390 for (siter
= summary_map
.begin(); siter
!= summary_map
.end(); ++siter
) {
1391 const rgw_usage_log_entry
& entry
= siter
->second
;
1392 formatter
->open_object_section("User");
1393 formatter
->dump_string("User", siter
->first
);
1394 dump_usage_categories_info(formatter
, entry
, &categories
);
1395 rgw_usage_data total_usage
;
1396 entry
.sum(total_usage
, categories
);
1397 formatter
->open_object_section("Total");
1398 encode_json("BytesSent", total_usage
.bytes_sent
, formatter
);
1399 encode_json("BytesReceived", total_usage
.bytes_received
, formatter
);
1400 encode_json("Ops", total_usage
.ops
, formatter
);
1401 encode_json("SuccessfulOps", total_usage
.successful_ops
, formatter
);
1402 formatter
->close_section(); // total
1403 formatter
->close_section(); // user
1406 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
1407 formatter
->open_object_section("Stats");
1410 encode_json("TotalBytes", stats
.size
, formatter
);
1411 encode_json("TotalBytesRounded", stats
.size_rounded
, formatter
);
1412 encode_json("TotalEntries", stats
.num_objects
, formatter
);
1414 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
1415 formatter
->close_section(); //Stats
1418 formatter
->close_section(); // summary
1421 formatter
->open_array_section("CapacityUsed");
1422 formatter
->open_object_section("User");
1423 formatter
->open_array_section("Buckets");
1424 for (const auto& biter
: buckets_usage
) {
1425 const cls_user_bucket_entry
& entry
= biter
.second
;
1426 dump_usage_bucket_info(formatter
, biter
.first
, entry
);
1428 formatter
->close_section(); // Buckets
1429 formatter
->close_section(); // User
1430 formatter
->close_section(); // CapacityUsed
1432 formatter
->close_section(); // usage
1433 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1436 int RGWListBucket_ObjStore_S3::get_common_params()
1438 list_versions
= s
->info
.args
.exists("versions");
1439 prefix
= s
->info
.args
.get("prefix");
1442 s
->info
.args
.get_bool("allow-unordered", &allow_unordered
, false);
1443 delimiter
= s
->info
.args
.get("delimiter");
1444 max_keys
= s
->info
.args
.get("max-keys");
1445 op_ret
= parse_max_keys();
1449 encoding_type
= s
->info
.args
.get("encoding-type");
1450 if (s
->system_request
) {
1451 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
1452 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
1455 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
1457 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
1461 shard_id
= s
->bucket_instance_shard_id
;
1467 int RGWListBucket_ObjStore_S3::get_params()
1469 int ret
= get_common_params();
1473 if (!list_versions
) {
1474 marker
= s
->info
.args
.get("marker");
1476 marker
.name
= s
->info
.args
.get("key-marker");
1477 marker
.instance
= s
->info
.args
.get("version-id-marker");
1482 int RGWListBucket_ObjStore_S3v2::get_params()
1484 int ret
= get_common_params();
1488 s
->info
.args
.get_bool("fetch-owner", &fetchOwner
, false);
1489 startAfter
= s
->info
.args
.get("start-after", &start_after_exist
);
1490 continuation_token
= s
->info
.args
.get("continuation-token", &continuation_token_exist
);
1491 if(!continuation_token_exist
) {
1492 marker
= startAfter
;
1494 marker
= continuation_token
;
1499 void RGWListBucket_ObjStore_S3::send_common_versioned_response()
1501 if (!s
->bucket_tenant
.empty()) {
1502 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1504 s
->formatter
->dump_string("Name", s
->bucket_name
);
1505 s
->formatter
->dump_string("Prefix", prefix
);
1506 s
->formatter
->dump_int("MaxKeys", max
);
1507 if (!delimiter
.empty()) {
1508 s
->formatter
->dump_string("Delimiter", delimiter
);
1510 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
1513 if (!common_prefixes
.empty()) {
1514 map
<string
, bool>::iterator pref_iter
;
1515 for (pref_iter
= common_prefixes
.begin();
1516 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
1517 s
->formatter
->open_array_section("CommonPrefixes");
1519 s
->formatter
->dump_string("Prefix", url_encode(pref_iter
->first
, false));
1521 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
1524 s
->formatter
->close_section();
1529 void RGWListBucket_ObjStore_S3::send_versioned_response()
1531 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
1532 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
1533 s
->formatter
->dump_string("EncodingType", "url");
1536 RGWListBucket_ObjStore_S3::send_common_versioned_response();
1537 s
->formatter
->dump_string("KeyMarker", marker
.name
);
1538 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
1539 if (is_truncated
&& !next_marker
.empty()) {
1540 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
1541 if (next_marker
.instance
.empty()) {
1542 s
->formatter
->dump_string("NextVersionIdMarker", "null");
1545 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
1550 if (objs_container
) {
1551 s
->formatter
->open_array_section("Entries");
1554 vector
<rgw_bucket_dir_entry
>::iterator iter
;
1555 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
1556 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
1558 s
->formatter
->open_object_section(section_name
);
1559 if (objs_container
) {
1560 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
1562 rgw_obj_key
key(iter
->key
);
1565 url_encode(key
.name
, key_name
);
1566 s
->formatter
->dump_string("Key", key_name
);
1569 s
->formatter
->dump_string("Key", key
.name
);
1571 string version_id
= key
.instance
;
1572 if (version_id
.empty()) {
1573 version_id
= "null";
1575 if (s
->system_request
) {
1576 if (iter
->versioned_epoch
> 0) {
1577 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
1579 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
1580 utime_t
ut(iter
->meta
.mtime
);
1581 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
1583 s
->formatter
->dump_string("VersionId", version_id
);
1584 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
1585 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
1586 if (!iter
->is_delete_marker()) {
1587 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
1588 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
1589 auto& storage_class
= rgw_placement_rule::get_canonical_storage_class(iter
->meta
.storage_class
);
1590 s
->formatter
->dump_string("StorageClass", storage_class
.c_str());
1592 dump_owner(s
, rgw_user(iter
->meta
.owner
), iter
->meta
.owner_display_name
);
1593 if (iter
->meta
.appendable
) {
1594 s
->formatter
->dump_string("Type", "Appendable");
1596 s
->formatter
->dump_string("Type", "Normal");
1598 s
->formatter
->close_section(); // Version/DeleteMarker
1600 if (objs_container
) {
1601 s
->formatter
->close_section(); // Entries
1603 s
->formatter
->close_section(); // ListVersionsResult
1605 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1609 void RGWListBucket_ObjStore_S3::send_common_response()
1611 if (!s
->bucket_tenant
.empty()) {
1612 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1614 s
->formatter
->dump_string("Name", s
->bucket_name
);
1615 s
->formatter
->dump_string("Prefix", prefix
);
1616 s
->formatter
->dump_int("MaxKeys", max
);
1617 if (!delimiter
.empty()) {
1618 s
->formatter
->dump_string("Delimiter", delimiter
);
1620 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
1623 if (!common_prefixes
.empty()) {
1624 map
<string
, bool>::iterator pref_iter
;
1625 for (pref_iter
= common_prefixes
.begin();
1626 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
1627 s
->formatter
->open_array_section("CommonPrefixes");
1629 s
->formatter
->dump_string("Prefix", url_encode(pref_iter
->first
, false));
1631 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
1633 s
->formatter
->close_section();
1638 void RGWListBucket_ObjStore_S3::send_response()
1641 set_req_state_err(s
, op_ret
);
1645 // Explicitly use chunked transfer encoding so that we can stream the result
1646 // to the user without having to wait for the full length of it.
1647 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
1652 if (list_versions
) {
1653 send_versioned_response();
1657 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
1658 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
1659 s
->formatter
->dump_string("EncodingType", "url");
1662 RGWListBucket_ObjStore_S3::send_common_response();
1664 vector
<rgw_bucket_dir_entry
>::iterator iter
;
1665 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
1666 rgw_obj_key
key(iter
->key
);
1667 s
->formatter
->open_array_section("Contents");
1670 url_encode(key
.name
, key_name
);
1671 s
->formatter
->dump_string("Key", key_name
);
1673 s
->formatter
->dump_string("Key", key
.name
);
1675 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
1676 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
1677 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
1678 auto& storage_class
= rgw_placement_rule::get_canonical_storage_class(iter
->meta
.storage_class
);
1679 s
->formatter
->dump_string("StorageClass", storage_class
.c_str());
1680 dump_owner(s
, rgw_user(iter
->meta
.owner
), iter
->meta
.owner_display_name
);
1681 if (s
->system_request
) {
1682 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
1684 if (iter
->meta
.appendable
) {
1685 s
->formatter
->dump_string("Type", "Appendable");
1687 s
->formatter
->dump_string("Type", "Normal");
1689 s
->formatter
->close_section();
1692 s
->formatter
->dump_string("Marker", marker
.name
);
1693 if (is_truncated
&& !next_marker
.empty()) {
1694 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
1696 s
->formatter
->close_section();
1697 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1700 void RGWListBucket_ObjStore_S3v2::send_versioned_response()
1702 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
1703 RGWListBucket_ObjStore_S3v2::send_common_versioned_response();
1704 s
->formatter
->dump_string("KeyContinuationToken", marker
.name
);
1705 s
->formatter
->dump_string("VersionIdContinuationToken", marker
.instance
);
1706 if (is_truncated
&& !next_marker
.empty()) {
1707 s
->formatter
->dump_string("NextKeyContinuationToken", next_marker
.name
);
1708 s
->formatter
->dump_string("NextVersionIdContinuationToken", next_marker
.instance
);
1711 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
1712 s
->formatter
->dump_string("EncodingType", "url");
1717 if (objs_container
) {
1718 s
->formatter
->open_array_section("Entries");
1721 vector
<rgw_bucket_dir_entry
>::iterator iter
;
1722 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
1723 const char *section_name
= (iter
->is_delete_marker() ? "DeleteContinuationToken"
1725 s
->formatter
->open_object_section(section_name
);
1726 if (objs_container
) {
1727 s
->formatter
->dump_bool("IsDeleteContinuationToken", iter
->is_delete_marker());
1729 rgw_obj_key
key(iter
->key
);
1732 url_encode(key
.name
, key_name
);
1733 s
->formatter
->dump_string("Key", key_name
);
1736 s
->formatter
->dump_string("Key", key
.name
);
1738 string version_id
= key
.instance
;
1739 if (version_id
.empty()) {
1740 version_id
= "null";
1742 if (s
->system_request
) {
1743 if (iter
->versioned_epoch
> 0) {
1744 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
1746 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
1747 utime_t
ut(iter
->meta
.mtime
);
1748 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
1750 s
->formatter
->dump_string("VersionId", version_id
);
1751 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
1752 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
1753 if (!iter
->is_delete_marker()) {
1754 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
1755 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
1756 auto& storage_class
= rgw_placement_rule::get_canonical_storage_class(iter
->meta
.storage_class
);
1757 s
->formatter
->dump_string("StorageClass", storage_class
.c_str());
1759 if (fetchOwner
== true) {
1760 dump_owner(s
, s
->user
->get_id(), s
->user
->get_display_name());
1762 s
->formatter
->close_section();
1766 if (objs_container
) {
1767 s
->formatter
->close_section();
1770 if (!common_prefixes
.empty()) {
1771 map
<string
, bool>::iterator pref_iter
;
1772 for (pref_iter
= common_prefixes
.begin();
1773 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
1774 s
->formatter
->open_array_section("CommonPrefixes");
1776 s
->formatter
->dump_string("Prefix", url_encode(pref_iter
->first
, false));
1778 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
1781 s
->formatter
->dump_int("KeyCount",objs
.size());
1782 if (start_after_exist
) {
1783 s
->formatter
->dump_string("StartAfter", startAfter
);
1785 s
->formatter
->close_section();
1789 s
->formatter
->close_section();
1790 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1794 void RGWListBucket_ObjStore_S3v2::send_response()
1797 set_req_state_err(s
, op_ret
);
1801 // Explicitly use chunked transfer encoding so that we can stream the result
1802 // to the user without having to wait for the full length of it.
1803 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
1808 if (list_versions
) {
1809 send_versioned_response();
1813 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
1814 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
1815 s
->formatter
->dump_string("EncodingType", "url");
1819 RGWListBucket_ObjStore_S3::send_common_response();
1821 vector
<rgw_bucket_dir_entry
>::iterator iter
;
1822 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
1823 rgw_obj_key
key(iter
->key
);
1824 s
->formatter
->open_array_section("Contents");
1827 url_encode(key
.name
, key_name
);
1828 s
->formatter
->dump_string("Key", key_name
);
1831 s
->formatter
->dump_string("Key", key
.name
);
1833 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
1834 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
1835 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
1836 auto& storage_class
= rgw_placement_rule::get_canonical_storage_class(iter
->meta
.storage_class
);
1837 s
->formatter
->dump_string("StorageClass", storage_class
.c_str());
1838 if (fetchOwner
== true) {
1839 dump_owner(s
, s
->user
->get_id(), s
->user
->get_display_name());
1841 if (s
->system_request
) {
1842 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
1844 if (iter
->meta
.appendable
) {
1845 s
->formatter
->dump_string("Type", "Appendable");
1847 s
->formatter
->dump_string("Type", "Normal");
1849 s
->formatter
->close_section();
1852 if (continuation_token_exist
) {
1853 s
->formatter
->dump_string("ContinuationToken", continuation_token
);
1855 if (is_truncated
&& !next_marker
.empty()) {
1856 s
->formatter
->dump_string("NextContinuationToken", next_marker
.name
);
1858 s
->formatter
->dump_int("KeyCount", objs
.size() + common_prefixes
.size());
1859 if (start_after_exist
) {
1860 s
->formatter
->dump_string("StartAfter", startAfter
);
1862 s
->formatter
->close_section();
1863 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1866 void RGWGetBucketLogging_ObjStore_S3::send_response()
1869 end_header(s
, this, "application/xml");
1872 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
1873 s
->formatter
->close_section();
1874 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1877 void RGWGetBucketLocation_ObjStore_S3::send_response()
1880 end_header(s
, this);
1883 RGWZoneGroup zonegroup
;
1886 int ret
= store
->svc()->zone
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
1888 api_name
= zonegroup
.api_name
;
1890 if (s
->bucket_info
.zonegroup
!= "default") {
1891 api_name
= s
->bucket_info
.zonegroup
;
1895 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
1896 "%s", api_name
.c_str());
1897 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1900 void RGWGetBucketVersioning_ObjStore_S3::send_response()
1903 end_header(s
, this, "application/xml");
1906 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
1908 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
1909 s
->formatter
->dump_string("Status", status
);
1910 const char *mfa_status
= (mfa_enabled
? "Enabled" : "Disabled");
1911 s
->formatter
->dump_string("MfaDelete", mfa_status
);
1913 s
->formatter
->close_section();
1914 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1917 struct ver_config_status
{
1918 int status
{VersioningSuspended
};
1924 } mfa_status
{MFA_UNKNOWN
};
1927 void decode_xml(XMLObj
*obj
) {
1930 RGWXMLDecoder::decode_xml("Status", status_str
, obj
);
1931 if (status_str
== "Enabled") {
1932 status
= VersioningEnabled
;
1933 } else if (status_str
!= "Suspended") {
1934 status
= VersioningStatusInvalid
;
1938 if (RGWXMLDecoder::decode_xml("MfaDelete", mfa_str
, obj
)) {
1939 if (mfa_str
== "Enabled") {
1940 mfa_status
= MFA_ENABLED
;
1941 } else if (mfa_str
== "Disabled") {
1942 mfa_status
= MFA_DISABLED
;
1950 int RGWSetBucketVersioning_ObjStore_S3::get_params()
1955 rgw_rest_read_all_input(s
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
1960 r
= do_aws4_auth_completion();
1965 RGWXMLDecoder::XMLParser parser
;
1966 if (!parser
.init()) {
1967 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl
;
1971 char* buf
= data
.c_str();
1972 if (!parser
.parse(buf
, data
.length(), 1)) {
1973 ldpp_dout(this, 10) << "NOTICE: failed to parse data: " << buf
<< dendl
;
1978 ver_config_status status_conf
;
1980 if (!RGWXMLDecoder::decode_xml("VersioningConfiguration", status_conf
, &parser
)) {
1981 ldpp_dout(this, 10) << "NOTICE: bad versioning config input" << dendl
;
1985 if (!store
->svc()->zone
->is_meta_master()) {
1986 /* only need to keep this data around if we're not meta master */
1987 in_data
.append(data
);
1990 versioning_status
= status_conf
.status
;
1991 if (versioning_status
== VersioningStatusInvalid
) {
1995 if (status_conf
.mfa_status
!= ver_config_status::MFA_UNKNOWN
) {
1996 mfa_set_status
= true;
1997 switch (status_conf
.mfa_status
) {
1998 case ver_config_status::MFA_DISABLED
:
2001 case ver_config_status::MFA_ENABLED
:
2005 ldpp_dout(this, 0) << "ERROR: RGWSetBucketVersioning_ObjStore_S3::get_params(): unexpected switch case mfa_status=" << status_conf
.mfa_status
<< dendl
;
2008 } else if (status_conf
.retcode
< 0) {
2009 r
= status_conf
.retcode
;
2014 void RGWSetBucketVersioning_ObjStore_S3::send_response()
2017 set_req_state_err(s
, op_ret
);
2019 end_header(s
, this, "application/xml");
2022 int RGWSetBucketWebsite_ObjStore_S3::get_params()
2024 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2028 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
2034 r
= do_aws4_auth_completion();
2039 in_data
.append(data
);
2041 RGWXMLDecoder::XMLParser parser
;
2042 if (!parser
.init()) {
2043 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl
;
2047 char* buf
= data
.c_str();
2048 if (!parser
.parse(buf
, data
.length(), 1)) {
2049 ldpp_dout(this, 5) << "failed to parse xml: " << buf
<< dendl
;
2054 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
2055 } catch (RGWXMLDecoder::err
& err
) {
2056 ldpp_dout(this, 5) << "unexpected xml: " << buf
<< dendl
;
2060 if (website_conf
.is_redirect_all
&& website_conf
.redirect_all
.hostname
.empty()) {
2061 s
->err
.message
= "A host name must be provided to redirect all requests (e.g. \"example.com\").";
2062 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2064 } else if (!website_conf
.is_redirect_all
&& !website_conf
.is_set_index_doc
) {
2065 s
->err
.message
= "A value for IndexDocument Suffix must be provided if RedirectAllRequestsTo is empty";
2066 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2068 } else if (!website_conf
.is_redirect_all
&& website_conf
.is_set_index_doc
&&
2069 website_conf
.index_doc_suffix
.empty()) {
2070 s
->err
.message
= "The IndexDocument Suffix is not well formed";
2071 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2075 #define WEBSITE_ROUTING_RULES_MAX_NUM 50
2076 int max_num
= s
->cct
->_conf
->rgw_website_routing_rules_max_num
;
2078 max_num
= WEBSITE_ROUTING_RULES_MAX_NUM
;
2080 int routing_rules_num
= website_conf
.routing_rules
.rules
.size();
2081 if (routing_rules_num
> max_num
) {
2082 ldpp_dout(this, 4) << "An website routing config can have up to "
2084 << " rules, request website routing rules num: "
2085 << routing_rules_num
<< dendl
;
2086 op_ret
= -ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR
;
2087 s
->err
.message
= std::to_string(routing_rules_num
) +" routing rules provided, the number of routing rules in a website configuration is limited to "
2088 + std::to_string(max_num
)
2090 return -ERR_INVALID_REQUEST
;
2096 void RGWSetBucketWebsite_ObjStore_S3::send_response()
2099 set_req_state_err(s
, op_ret
);
2101 end_header(s
, this, "application/xml");
2104 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
2107 op_ret
= STATUS_NO_CONTENT
;
2109 set_req_state_err(s
, op_ret
);
2111 end_header(s
, this, "application/xml");
2114 void RGWGetBucketWebsite_ObjStore_S3::send_response()
2117 set_req_state_err(s
, op_ret
);
2119 end_header(s
, this, "application/xml");
2126 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
2128 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
2129 conf
.dump_xml(s
->formatter
);
2130 s
->formatter
->close_section(); // WebsiteConfiguration
2131 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2134 static void dump_bucket_metadata(struct req_state
*s
, rgw::sal::RGWBucket
* bucket
)
2136 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
->get_count()));
2137 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
->get_size()));
2140 void RGWStatBucket_ObjStore_S3::send_response()
2143 dump_bucket_metadata(s
, bucket
);
2146 set_req_state_err(s
, op_ret
);
2149 end_header(s
, this);
2153 static int create_s3_policy(struct req_state
*s
, rgw::sal::RGWRadosStore
*store
,
2154 RGWAccessControlPolicy_S3
& s3policy
,
2157 if (s
->has_acl_header
) {
2158 if (!s
->canned_acl
.empty())
2159 return -ERR_INVALID_REQUEST
;
2161 return s3policy
.create_from_headers(store
->ctl()->user
, s
->info
.env
, owner
);
2164 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
2167 class RGWLocationConstraint
: public XMLObj
2170 RGWLocationConstraint() {}
2171 ~RGWLocationConstraint() override
{}
2172 bool xml_end(const char *el
) override
{
2176 location_constraint
= get_data();
2181 string location_constraint
;
2184 class RGWCreateBucketConfig
: public XMLObj
2187 RGWCreateBucketConfig() {}
2188 ~RGWCreateBucketConfig() override
{}
2191 class RGWCreateBucketParser
: public RGWXMLParser
2193 XMLObj
*alloc_obj(const char *el
) override
{
2198 RGWCreateBucketParser() {}
2199 ~RGWCreateBucketParser() override
{}
2201 bool get_location_constraint(string
& zone_group
) {
2202 XMLObj
*config
= find_first("CreateBucketConfiguration");
2206 XMLObj
*constraint
= config
->find_first("LocationConstraint");
2210 zone_group
= constraint
->get_data();
2216 int RGWCreateBucket_ObjStore_S3::get_params()
2218 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2219 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
2221 int r
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
2225 r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2231 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2235 std::tie(op_ret
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
2237 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
2240 const int auth_ret
= do_aws4_auth_completion();
2245 in_data
.append(data
);
2247 if (data
.length()) {
2248 RGWCreateBucketParser parser
;
2250 if (!parser
.init()) {
2251 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl
;
2255 char* buf
= data
.c_str();
2256 bool success
= parser
.parse(buf
, data
.length(), 1);
2257 ldpp_dout(this, 20) << "create bucket input data=" << buf
<< dendl
;
2260 ldpp_dout(this, 0) << "failed to parse input: " << buf
<< dendl
;
2264 if (!parser
.get_location_constraint(location_constraint
)) {
2265 ldpp_dout(this, 0) << "provided input did not specify location constraint correctly" << dendl
;
2269 ldpp_dout(this, 10) << "create bucket location constraint: "
2270 << location_constraint
<< dendl
;
2273 size_t pos
= location_constraint
.find(':');
2274 if (pos
!= string::npos
) {
2275 placement_rule
.init(location_constraint
.substr(pos
+ 1), s
->info
.storage_class
);
2276 location_constraint
= location_constraint
.substr(0, pos
);
2278 placement_rule
.storage_class
= s
->info
.storage_class
;
2280 auto iter
= s
->info
.x_meta_map
.find("x-amz-bucket-object-lock-enabled");
2281 if (iter
!= s
->info
.x_meta_map
.end()) {
2282 if (!boost::algorithm::iequals(iter
->second
, "true") && !boost::algorithm::iequals(iter
->second
, "false")) {
2285 obj_lock_enabled
= boost::algorithm::iequals(iter
->second
, "true");
2290 void RGWCreateBucket_ObjStore_S3::send_response()
2292 if (op_ret
== -ERR_BUCKET_EXISTS
)
2295 set_req_state_err(s
, op_ret
);
2302 if (s
->system_request
) {
2303 JSONFormatter f
; /* use json formatter for system requests output */
2305 f
.open_object_section("info");
2306 encode_json("entry_point_object_ver", ep_objv
, &f
);
2307 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
2308 encode_json("bucket_info", info
, &f
);
2310 rgw_flush_formatter_and_reset(s
, &f
);
2314 void RGWDeleteBucket_ObjStore_S3::send_response()
2318 r
= STATUS_NO_CONTENT
;
2320 set_req_state_err(s
, r
);
2322 end_header(s
, this);
2325 static inline void map_qs_metadata(struct req_state
* s
)
2327 /* merge S3 valid user metadata from the query-string into
2328 * x_meta_map, which maps them to attributes */
2329 const auto& params
= const_cast<RGWHTTPArgs
&>(s
->info
.args
).get_params();
2330 for (const auto& elt
: params
) {
2331 std::string k
= boost::algorithm::to_lower_copy(elt
.first
);
2332 if (k
.find("x-amz-meta-") == /* offset */ 0) {
2333 rgw_add_amz_meta_header(s
->info
.x_meta_map
, k
, elt
.second
);
2338 int RGWPutObj_ObjStore_S3::get_params()
2341 return -ERR_LENGTH_REQUIRED
;
2343 map
<string
, bufferlist
> src_attrs
;
2349 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2350 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2356 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
2357 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
2358 copy_source
= url_decode(s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE", ""));
2359 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
2361 /* handle x-amz-copy-source */
2362 boost::string_view
cs_view(copy_source
);
2363 if (! cs_view
.empty()) {
2364 if (cs_view
[0] == '/')
2365 cs_view
.remove_prefix(1);
2366 copy_source_bucket_name
= cs_view
.to_string();
2367 pos
= copy_source_bucket_name
.find("/");
2368 if (pos
== std::string::npos
) {
2370 ldpp_dout(this, 5) << "x-amz-copy-source bad format" << dendl
;
2373 copy_source_object_name
=
2374 copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
2375 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
2376 #define VERSION_ID_STR "?versionId="
2377 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
2378 if (pos
== std::string::npos
) {
2379 copy_source_object_name
= url_decode(copy_source_object_name
);
2381 copy_source_version_id
=
2382 copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
2383 copy_source_object_name
=
2384 url_decode(copy_source_object_name
.substr(0, pos
));
2386 pos
= copy_source_bucket_name
.find(":");
2387 if (pos
== std::string::npos
) {
2388 copy_source_tenant_name
= s
->src_tenant_name
;
2390 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
2391 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
2392 if (copy_source_bucket_name
.empty()) {
2394 ldpp_dout(this, 5) << "source bucket name is empty" << dendl
;
2398 ret
= store
->getRados()->get_bucket_info(store
->svc(),
2399 copy_source_tenant_name
,
2400 copy_source_bucket_name
,
2401 copy_source_bucket_info
,
2402 NULL
, s
->yield
, &src_attrs
);
2404 ldpp_dout(this, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
2408 /* handle x-amz-copy-source-range */
2410 if (copy_source_range
) {
2411 string range
= copy_source_range
;
2412 pos
= range
.find("bytes=");
2413 if (pos
== std::string::npos
|| pos
!= 0) {
2415 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl
;
2418 /* 6 is the length of "bytes=" */
2419 range
= range
.substr(pos
+ 6);
2420 pos
= range
.find("-");
2421 if (pos
== std::string::npos
) {
2423 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl
;
2426 string first
= range
.substr(0, pos
);
2427 string last
= range
.substr(pos
+ 1);
2428 if (first
.find_first_not_of("0123456789") != std::string::npos
|| last
.find_first_not_of("0123456789") != std::string::npos
)
2430 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format not an integer" << dendl
;
2434 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
2435 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
2436 if (copy_source_range_fst
> copy_source_range_lst
)
2439 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format first number bigger than second" << dendl
;
2446 /* handle object tagging */
2447 auto tag_str
= s
->info
.env
->get("HTTP_X_AMZ_TAGGING");
2449 obj_tags
= std::make_unique
<RGWObjTags
>();
2450 ret
= obj_tags
->set_from_string(tag_str
);
2452 ldpp_dout(this,0) << "setting obj tags failed with " << ret
<< dendl
;
2453 if (ret
== -ERR_INVALID_TAG
){
2454 ret
= -EINVAL
; //s3 returns only -EINVAL for PUT requests
2461 //handle object lock
2462 auto obj_lock_mode_str
= s
->info
.env
->get("HTTP_X_AMZ_OBJECT_LOCK_MODE");
2463 auto obj_lock_date_str
= s
->info
.env
->get("HTTP_X_AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE");
2464 auto obj_legal_hold_str
= s
->info
.env
->get("HTTP_X_AMZ_OBJECT_LOCK_LEGAL_HOLD");
2465 if (obj_lock_mode_str
&& obj_lock_date_str
) {
2466 boost::optional
<ceph::real_time
> date
= ceph::from_iso_8601(obj_lock_date_str
);
2467 if (boost::none
== date
|| ceph::real_clock::to_time_t(*date
) <= ceph_clock_now()) {
2469 ldpp_dout(this,0) << "invalid x-amz-object-lock-retain-until-date value" << dendl
;
2472 if (strcmp(obj_lock_mode_str
, "GOVERNANCE") != 0 && strcmp(obj_lock_mode_str
, "COMPLIANCE") != 0) {
2474 ldpp_dout(this,0) << "invalid x-amz-object-lock-mode value" << dendl
;
2477 obj_retention
= new RGWObjectRetention(obj_lock_mode_str
, *date
);
2478 } else if ((obj_lock_mode_str
&& !obj_lock_date_str
) || (!obj_lock_mode_str
&& obj_lock_date_str
)) {
2480 ldpp_dout(this,0) << "need both x-amz-object-lock-mode and x-amz-object-lock-retain-until-date " << dendl
;
2483 if (obj_legal_hold_str
) {
2484 if (strcmp(obj_legal_hold_str
, "ON") != 0 && strcmp(obj_legal_hold_str
, "OFF") != 0) {
2486 ldpp_dout(this,0) << "invalid x-amz-object-lock-legal-hold value" << dendl
;
2489 obj_legal_hold
= new RGWObjectLegalHold(obj_legal_hold_str
);
2491 if (!s
->bucket_info
.obj_lock_enabled() && (obj_retention
|| obj_legal_hold
)) {
2492 ldpp_dout(this, 0) << "ERROR: object retention or legal hold can't be set if bucket object lock not configured" << dendl
;
2493 ret
= -ERR_INVALID_REQUEST
;
2496 multipart_upload_id
= s
->info
.args
.get("uploadId");
2497 multipart_part_str
= s
->info
.args
.get("partNumber");
2498 if (!multipart_part_str
.empty()) {
2500 multipart_part_num
= strict_strtol(multipart_part_str
.c_str(), 10, &err
);
2502 ldpp_dout(s
, 10) << "bad part number: " << multipart_part_str
<< ": " << err
<< dendl
;
2505 } else if (!multipart_upload_id
.empty()) {
2506 ldpp_dout(s
, 10) << "part number with no multipart upload id" << dendl
;
2510 append
= s
->info
.args
.exists("append");
2512 string pos_str
= s
->info
.args
.get("position");
2513 if (pos_str
.empty()) {
2516 position
= strtoull(pos_str
.c_str(), NULL
, 10);
2520 return RGWPutObj_ObjStore::get_params();
2523 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
2525 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
2527 const int ret_auth
= do_aws4_auth_completion();
2536 static int get_success_retcode(int code
)
2540 return STATUS_CREATED
;
2542 return STATUS_NO_CONTENT
;
2547 void RGWPutObj_ObjStore_S3::send_response()
2550 set_req_state_err(s
, op_ret
);
2553 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
2554 op_ret
= get_success_retcode(
2555 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
2556 set_req_state_err(s
, op_ret
);
2559 string expires
= get_s3_expiration_header(s
, mtime
);
2561 if (copy_source
.empty()) {
2564 dump_content_length(s
, 0);
2565 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
2566 dump_header_if_nonempty(s
, "x-amz-expiration", expires
);
2567 for (auto &it
: crypt_http_responses
)
2568 dump_header(s
, it
.first
, it
.second
);
2571 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
2572 dump_header_if_nonempty(s
, "x-amz-expiration", expires
);
2573 end_header(s
, this, "application/xml");
2577 time_t secs
= (time_t)ut
.sec();
2578 gmtime_r(&secs
, &tmp
);
2579 char buf
[TIME_BUF_SIZE
];
2580 s
->formatter
->open_object_section_in_ns("CopyPartResult",
2581 "http://s3.amazonaws.com/doc/2006-03-01/");
2582 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
2583 s
->formatter
->dump_string("LastModified", buf
);
2585 s
->formatter
->dump_string("ETag", etag
);
2586 s
->formatter
->close_section();
2587 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2592 if (op_ret
== 0 || op_ret
== -ERR_POSITION_NOT_EQUAL_TO_LENGTH
) {
2593 dump_header(s
, "x-rgw-next-append-position", cur_accounted_size
);
2596 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
2597 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
2599 end_header(s
, this);
2602 static inline int get_obj_attrs(rgw::sal::RGWRadosStore
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
2604 RGWRados::Object
op_target(store
->getRados(), s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
2605 RGWRados::Object::Read
read_op(&op_target
);
2607 read_op
.params
.attrs
= &attrs
;
2609 return read_op
.prepare(s
->yield
);
2612 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
2616 attrs
.emplace(key
, std::move(bl
));
2619 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
2623 attrs
.emplace(key
, std::move(bl
));
2626 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
2627 std::unique_ptr
<RGWGetObj_Filter
>* filter
,
2628 RGWGetObj_Filter
* cb
,
2629 map
<string
, bufferlist
>& attrs
,
2630 bufferlist
* manifest_bl
)
2632 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
2635 std::unique_ptr
<BlockCrypt
> block_crypt
;
2636 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
2638 if (block_crypt
!= nullptr) {
2639 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
2640 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
2642 if (manifest_bl
!= nullptr) {
2643 res
= f
->read_manifest(*manifest_bl
);
2645 *filter
= std::move(f
);
2654 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
2655 std::unique_ptr
<rgw::putobj::DataProcessor
> *filter
,
2656 rgw::putobj::DataProcessor
*cb
)
2659 if (!multipart_upload_id
.empty()) {
2660 RGWMPObj
mp(s
->object
.name
, multipart_upload_id
);
2662 obj
.init_ns(s
->bucket
, mp
.get_meta(), RGW_OBJ_NS_MULTIPART
);
2663 obj
.set_in_extra_data(true);
2664 map
<string
, bufferlist
> xattrs
;
2665 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
2667 std::unique_ptr
<BlockCrypt
> block_crypt
;
2668 /* We are adding to existing object.
2669 * We use crypto mode that configured as if we were decrypting. */
2670 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &block_crypt
, crypt_http_responses
);
2671 if (res
== 0 && block_crypt
!= nullptr)
2672 filter
->reset(new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
2674 /* it is ok, to not have encryption at all */
2678 std::unique_ptr
<BlockCrypt
> block_crypt
;
2679 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
2680 if (res
== 0 && block_crypt
!= nullptr) {
2681 filter
->reset(new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
2687 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
2689 static string var
= "${filename}";
2690 int pos
= key
.find(var
);
2694 string new_key
= key
.substr(0, pos
);
2695 new_key
.append(filename
);
2696 new_key
.append(key
.substr(pos
+ var
.size()));
2701 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
2703 return s
->object
.name
;
2706 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
2708 return content_type
;
2711 int RGWPostObj_ObjStore_S3::get_params()
2713 op_ret
= RGWPostObj_ObjStore::get_params();
2720 ldpp_dout(this, 20) << "adding bucket to policy env: " << s
->bucket
.name
2722 env
.add_var("bucket", s
->bucket
.name
);
2726 struct post_form_part part
;
2727 int r
= read_form_part_header(&part
, done
);
2731 if (s
->cct
->_conf
->subsys
.should_gather
<ceph_subsys_rgw
, 20>()) {
2732 ldpp_dout(this, 20) << "read part header -- part.name="
2733 << part
.name
<< dendl
;
2735 for (const auto& pair
: part
.fields
) {
2736 ldpp_dout(this, 20) << "field.name=" << pair
.first
<< dendl
;
2737 ldpp_dout(this, 20) << "field.val=" << pair
.second
.val
<< dendl
;
2738 ldpp_dout(this, 20) << "field.params:" << dendl
;
2740 for (const auto& param_pair
: pair
.second
.params
) {
2741 ldpp_dout(this, 20) << " " << param_pair
.first
2742 << " -> " << param_pair
.second
<< dendl
;
2747 if (done
) { /* unexpected here */
2748 err_msg
= "Malformed request";
2752 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
2753 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
2754 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
2755 if (iter
!= field
.params
.end()) {
2756 filename
= iter
->second
;
2758 parts
[part
.name
] = part
;
2763 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
2764 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
2765 if (r
< 0 || !boundary
) {
2766 err_msg
= "Couldn't find boundary";
2769 parts
[part
.name
] = part
;
2770 string
part_str(part
.data
.c_str(), part
.data
.length());
2771 env
.add_var(part
.name
, part_str
);
2775 if (!part_str(parts
, "key", &object_str
)) {
2776 err_msg
= "Key not specified";
2780 s
->object
= rgw_obj_key(object_str
);
2782 rebuild_key(s
->object
.name
);
2784 if (s
->object
.empty()) {
2785 err_msg
= "Empty object name";
2789 env
.add_var("key", s
->object
.name
);
2791 part_str(parts
, "Content-Type", &content_type
);
2793 /* AWS permits POST without Content-Type: http://tracker.ceph.com/issues/20201 */
2794 if (! content_type
.empty()) {
2795 env
.add_var("Content-Type", content_type
);
2798 std::string storage_class
;
2799 part_str(parts
, "x-amz-storage-class", &storage_class
);
2801 if (! storage_class
.empty()) {
2802 s
->dest_placement
.storage_class
= storage_class
;
2803 if (!store
->svc()->zone
->get_zone_params().valid_placement(s
->dest_placement
)) {
2804 ldpp_dout(this, 0) << "NOTICE: invalid dest placement: " << s
->dest_placement
.to_str() << dendl
;
2805 err_msg
= "The storage class you specified is not valid";
2810 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
2811 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
2812 for (; piter
!= parts
.end(); ++piter
) {
2813 string n
= piter
->first
;
2814 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
2815 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
2818 string attr_name
= RGW_ATTR_PREFIX
;
2819 attr_name
.append(n
);
2821 /* need to null terminate it */
2822 bufferlist
& data
= piter
->second
.data
;
2823 string str
= string(data
.c_str(), data
.length());
2826 attr_bl
.append(str
.c_str(), str
.size() + 1);
2828 attrs
[attr_name
] = attr_bl
;
2830 // TODO: refactor this and the above loop to share code
2831 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
2832 if (piter
!= parts
.end()) {
2833 string n
= piter
->first
;
2834 string attr_name
= RGW_ATTR_PREFIX
;
2835 attr_name
.append(n
);
2836 /* need to null terminate it */
2837 bufferlist
& data
= piter
->second
.data
;
2838 string str
= string(data
.c_str(), data
.length());
2841 attr_bl
.append(str
.c_str(), str
.size() + 1);
2843 attrs
[attr_name
] = attr_bl
;
2846 int r
= get_policy();
2855 min_len
= post_policy
.min_length
;
2856 max_len
= post_policy
.max_length
;
2863 int RGWPostObj_ObjStore_S3::get_tags()
2866 if (part_str(parts
, "tagging", &tags_str
)) {
2867 RGWXMLParser parser
;
2868 if (!parser
.init()){
2869 ldpp_dout(this, 0) << "Couldn't init RGWObjTags XML parser" << dendl
;
2870 err_msg
= "Server couldn't process the request";
2871 return -EINVAL
; // TODO: This class of errors in rgw code should be a 5XX error
2873 if (!parser
.parse(tags_str
.c_str(), tags_str
.size(), 1)) {
2874 ldpp_dout(this,0 ) << "Invalid Tagging XML" << dendl
;
2875 err_msg
= "Invalid Tagging XML";
2879 RGWObjTagging_S3 tagging
;
2882 RGWXMLDecoder::decode_xml("Tagging", tagging
, &parser
);
2883 } catch (RGWXMLDecoder::err
& err
) {
2884 ldpp_dout(this, 5) << "Malformed tagging request: " << err
<< dendl
;
2888 RGWObjTags obj_tags
;
2889 int r
= tagging
.rebuild(obj_tags
);
2894 obj_tags
.encode(tags_bl
);
2895 ldpp_dout(this, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
2896 attrs
[RGW_ATTR_TAGS
] = tags_bl
;
2903 int RGWPostObj_ObjStore_S3::get_policy()
2905 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
2906 bool aws4_auth
= false;
2908 /* x-amz-algorithm handling */
2909 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
2910 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
2911 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
2912 ldpp_dout(this, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
2915 ldpp_dout(this, 0) << "Signature verification algorithm AWS v2" << dendl
;
2918 // check that the signature matches the encoded policy
2922 /* x-amz-credential handling */
2923 if (!part_str(parts
, "x-amz-credential",
2924 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
2925 ldpp_dout(this, 0) << "No S3 aws4 credential found!" << dendl
;
2926 err_msg
= "Missing aws4 credential";
2930 /* x-amz-signature handling */
2931 if (!part_str(parts
, "x-amz-signature",
2932 &s
->auth
.s3_postobj_creds
.signature
)) {
2933 ldpp_dout(this, 0) << "No aws4 signature found!" << dendl
;
2934 err_msg
= "Missing aws4 signature";
2938 /* x-amz-date handling */
2939 std::string received_date_str
;
2940 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
2941 ldpp_dout(this, 0) << "No aws4 date found!" << dendl
;
2942 err_msg
= "Missing aws4 date";
2948 // check that the signature matches the encoded policy
2949 if (!part_str(parts
, "AWSAccessKeyId",
2950 &s
->auth
.s3_postobj_creds
.access_key
)) {
2951 ldpp_dout(this, 0) << "No S3 aws2 access key found!" << dendl
;
2952 err_msg
= "Missing aws2 access key";
2956 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
2957 ldpp_dout(this, 0) << "No aws2 signature found!" << dendl
;
2958 err_msg
= "Missing aws2 signature";
2963 if (part_str(parts
, "x-amz-security-token", &s
->auth
.s3_postobj_creds
.x_amz_security_token
)) {
2964 if (s
->auth
.s3_postobj_creds
.x_amz_security_token
.size() == 0) {
2965 err_msg
= "Invalid token";
2970 /* FIXME: this is a makeshift solution. The browser upload authentication will be
2971 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
2973 const int ret
= rgw::auth::Strategy::apply(this, auth_registry_ptr
->get_s3_post(), s
);
2977 /* Populate the owner info. */
2978 s
->owner
.set_id(s
->user
->get_id());
2979 s
->owner
.set_name(s
->user
->get_display_name());
2980 ldpp_dout(this, 20) << "Successful Signature Verification!" << dendl
;
2983 ceph::bufferlist decoded_policy
;
2985 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
2986 } catch (buffer::error
& err
) {
2987 ldpp_dout(this, 0) << "failed to decode_base64 policy" << dendl
;
2988 err_msg
= "Could not decode policy";
2992 decoded_policy
.append('\0'); // NULL terminate
2993 ldpp_dout(this, 20) << "POST policy: " << decoded_policy
.c_str() << dendl
;
2996 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
2998 if (err_msg
.empty()) {
2999 err_msg
= "Failed to parse policy";
3001 ldpp_dout(this, 0) << "failed to parse policy" << dendl
;
3007 post_policy
.set_var_checked("x-amz-signature");
3010 post_policy
.set_var_checked("AWSAccessKeyId");
3011 post_policy
.set_var_checked("signature");
3013 post_policy
.set_var_checked("policy");
3015 r
= post_policy
.check(&env
, err_msg
);
3017 if (err_msg
.empty()) {
3018 err_msg
= "Policy check failed";
3020 ldpp_dout(this, 0) << "policy check failed" << dendl
;
3025 ldpp_dout(this, 0) << "No attached policy found!" << dendl
;
3029 part_str(parts
, "acl", &canned_acl
);
3031 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
3032 ldpp_dout(this, 20) << "canned_acl=" << canned_acl
<< dendl
;
3033 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
3034 err_msg
= "Bad canned ACLs";
3043 int RGWPostObj_ObjStore_S3::complete_get_params()
3047 struct post_form_part part
;
3048 int r
= read_form_part_header(&part
, done
);
3053 ceph::bufferlist part_data
;
3055 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
3056 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
3057 if (r
< 0 || !boundary
) {
3061 /* Just reading the data but not storing any results of that. */
3067 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
3072 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
3073 int r
= read_data(bl
, chunk_size
, boundary
, done
);
3080 /* Reached end of data, let's drain the rest of the params */
3081 r
= complete_get_params();
3092 void RGWPostObj_ObjStore_S3::send_response()
3094 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
3097 part_str(parts
, "success_action_redirect", &redirect
);
3102 string etag_str
= "\"";
3104 etag_str
.append(etag
);
3105 etag_str
.append("\"");
3109 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
3110 url_encode(s
->bucket_name
, bucket
);
3111 url_encode(s
->object
.name
, key
);
3112 url_encode(etag_str
, etag_url
);
3114 if (!s
->bucket_tenant
.empty()) {
3116 * What we really would like is to quaily the bucket name, so
3117 * that the client could simply copy it and paste into next request.
3118 * Unfortunately, in S3 we cannot know if the client will decide
3119 * to come through DNS, with "bucket.tenant" sytanx, or through
3120 * URL with "tenant\bucket" syntax. Therefore, we provide the
3121 * tenant separately.
3123 redirect
.append("?tenant=");
3124 redirect
.append(tenant
);
3125 redirect
.append("&bucket=");
3126 redirect
.append(bucket
);
3128 redirect
.append("?bucket=");
3129 redirect
.append(bucket
);
3131 redirect
.append("&key=");
3132 redirect
.append(key
);
3133 redirect
.append("&etag=");
3134 redirect
.append(etag_url
);
3136 int r
= check_utf8(redirect
.c_str(), redirect
.size());
3141 dump_redirect(s
, redirect
);
3142 op_ret
= STATUS_REDIRECT
;
3143 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
3144 string status_string
;
3145 uint32_t status_int
;
3147 part_str(parts
, "success_action_status", &status_string
);
3149 int r
= stringtoul(status_string
, &status_int
);
3155 switch (status_int
) {
3159 op_ret
= STATUS_CREATED
;
3162 op_ret
= STATUS_NO_CONTENT
;
3165 } else if (! op_ret
) {
3166 op_ret
= STATUS_NO_CONTENT
;
3170 if (op_ret
== STATUS_CREATED
) {
3171 for (auto &it
: crypt_http_responses
)
3172 dump_header(s
, it
.first
, it
.second
);
3173 s
->formatter
->open_object_section("PostResponse");
3174 std::string base_uri
= compute_domain_uri(s
);
3175 if (!s
->bucket_tenant
.empty()){
3176 s
->formatter
->dump_format("Location", "%s/%s:%s/%s",
3178 url_encode(s
->bucket_tenant
).c_str(),
3179 url_encode(s
->bucket_name
).c_str(),
3180 url_encode(s
->object
.name
).c_str());
3181 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
3183 s
->formatter
->dump_format("Location", "%s/%s/%s",
3185 url_encode(s
->bucket_name
).c_str(),
3186 url_encode(s
->object
.name
).c_str());
3188 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
3189 s
->formatter
->dump_string("Key", s
->object
.name
);
3190 s
->formatter
->dump_string("ETag", etag
);
3191 s
->formatter
->close_section();
3193 s
->err
.message
= err_msg
;
3194 set_req_state_err(s
, op_ret
);
3197 dump_content_length(s
, s
->formatter
->get_len());
3199 end_header(s
, this);
3200 if (op_ret
!= STATUS_CREATED
)
3203 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3206 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
3207 std::unique_ptr
<rgw::putobj::DataProcessor
> *filter
,
3208 rgw::putobj::DataProcessor
*cb
)
3210 std::unique_ptr
<BlockCrypt
> block_crypt
;
3211 int res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
,
3212 crypt_http_responses
);
3213 if (res
== 0 && block_crypt
!= nullptr) {
3214 filter
->reset(new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
3219 int RGWDeleteObj_ObjStore_S3::get_params()
3221 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
3223 if (s
->system_request
) {
3224 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
3228 std::string if_unmod_decoded
= url_decode(if_unmod
);
3231 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
3232 ldpp_dout(this, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
3235 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
3238 const char *bypass_gov_header
= s
->info
.env
->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
3239 if (bypass_gov_header
) {
3240 std::string bypass_gov_decoded
= url_decode(bypass_gov_header
);
3241 bypass_governance_mode
= boost::algorithm::iequals(bypass_gov_decoded
, "true");
3247 void RGWDeleteObj_ObjStore_S3::send_response()
3253 r
= STATUS_NO_CONTENT
;
3255 set_req_state_err(s
, r
);
3257 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
3258 if (delete_marker
) {
3259 dump_header(s
, "x-amz-delete-marker", "true");
3261 end_header(s
, this);
3264 int RGWCopyObj_ObjStore_S3::init_dest_policy()
3266 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
3268 /* build a policy for the target object */
3269 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
3273 dest_policy
= s3policy
;
3278 int RGWCopyObj_ObjStore_S3::get_params()
3280 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
3281 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
3282 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
3283 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
3285 src_tenant_name
= s
->src_tenant_name
;
3286 src_bucket_name
= s
->src_bucket_name
;
3287 src_object
= s
->src_object
;
3288 dest_tenant_name
= s
->bucket
.tenant
;
3289 dest_bucket_name
= s
->bucket
.name
;
3290 dest_object
= s
->object
.name
;
3292 if (s
->system_request
) {
3293 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
3294 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
3297 copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3298 auto tmp_md_d
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
3300 if (strcasecmp(tmp_md_d
, "COPY") == 0) {
3301 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
3302 } else if (strcasecmp(tmp_md_d
, "REPLACE") == 0) {
3303 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
3304 } else if (!source_zone
.empty()) {
3305 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
3307 s
->err
.message
= "Unknown metadata directive.";
3308 ldpp_dout(this, 0) << s
->err
.message
<< dendl
;
3311 md_directive
= tmp_md_d
;
3314 if (source_zone
.empty() &&
3315 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
3316 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
3317 (dest_object
.compare(src_object
.name
) == 0) &&
3318 src_object
.instance
.empty() &&
3319 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
3320 need_to_check_storage_class
= true;
3326 int RGWCopyObj_ObjStore_S3::check_storage_class(const rgw_placement_rule
& src_placement
)
3328 if (src_placement
== s
->dest_placement
) {
3329 /* can only copy object into itself if replacing attrs */
3330 s
->err
.message
= "This copy request is illegal because it is trying to copy "
3331 "an object to itself without changing the object's metadata, "
3332 "storage class, website redirect location or encryption attributes.";
3333 ldpp_dout(this, 0) << s
->err
.message
<< dendl
;
3334 return -ERR_INVALID_REQUEST
;
3339 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
3341 if (! sent_header
) {
3343 set_req_state_err(s
, op_ret
);
3346 // Explicitly use chunked transfer encoding so that we can stream the result
3347 // to the user without having to wait for the full length of it.
3348 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
3351 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
3355 /* Send progress field. Note that this diverge from the original S3
3356 * spec. We do this in order to keep connection alive.
3358 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
3360 rgw_flush_formatter(s
, s
->formatter
);
3363 void RGWCopyObj_ObjStore_S3::send_response()
3366 send_partial_response(0);
3369 dump_time(s
, "LastModified", &mtime
);
3370 if (!etag
.empty()) {
3371 s
->formatter
->dump_string("ETag", std::move(etag
));
3373 s
->formatter
->close_section();
3374 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3378 void RGWGetACLs_ObjStore_S3::send_response()
3381 set_req_state_err(s
, op_ret
);
3383 end_header(s
, this, "application/xml");
3385 rgw_flush_formatter(s
, s
->formatter
);
3389 int RGWPutACLs_ObjStore_S3::get_params()
3391 int ret
= RGWPutACLs_ObjStore::get_params();
3393 const int ret_auth
= do_aws4_auth_completion();
3398 /* a request body is not required an S3 PutACLs request--n.b.,
3399 * s->length is non-null iff a content length was parsed (the
3400 * ACP or canned ACL could be in any of 3 headers, don't worry
3401 * about that here) */
3402 if ((ret
== -ERR_LENGTH_REQUIRED
) &&
3410 int RGWPutACLs_ObjStore_S3::get_policy_from_state(rgw::sal::RGWRadosStore
*store
,
3411 struct req_state
*s
,
3414 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
3416 // bucket-* canned acls do not apply to bucket
3417 if (s
->object
.empty()) {
3418 if (s
->canned_acl
.find("bucket") != string::npos
)
3419 s
->canned_acl
.clear();
3422 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
3426 s3policy
.to_xml(ss
);
3431 void RGWPutACLs_ObjStore_S3::send_response()
3434 set_req_state_err(s
, op_ret
);
3436 end_header(s
, this, "application/xml");
3440 void RGWGetLC_ObjStore_S3::execute()
3442 config
.set_ctx(s
->cct
);
3444 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
3445 if (aiter
== s
->bucket_attrs
.end()) {
3450 bufferlist::const_iterator iter
{&aiter
->second
};
3452 config
.decode(iter
);
3453 } catch (const buffer::error
& e
) {
3454 ldpp_dout(this, 0) << __func__
<< "decode life cycle config failed" << dendl
;
3460 void RGWGetLC_ObjStore_S3::send_response()
3463 if (op_ret
== -ENOENT
) {
3464 set_req_state_err(s
, ERR_NO_SUCH_LC
);
3466 set_req_state_err(s
, op_ret
);
3470 end_header(s
, this, "application/xml");
3476 encode_xml("LifecycleConfiguration", XMLNS_AWS_S3
, config
, s
->formatter
);
3477 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3480 void RGWPutLC_ObjStore_S3::send_response()
3483 set_req_state_err(s
, op_ret
);
3485 end_header(s
, this, "application/xml");
3489 void RGWDeleteLC_ObjStore_S3::send_response()
3492 op_ret
= STATUS_NO_CONTENT
;
3494 set_req_state_err(s
, op_ret
);
3497 end_header(s
, this, "application/xml");
3501 void RGWGetCORS_ObjStore_S3::send_response()
3504 if (op_ret
== -ENOENT
)
3505 set_req_state_err(s
, ERR_NO_SUCH_CORS_CONFIGURATION
);
3507 set_req_state_err(s
, op_ret
);
3510 end_header(s
, NULL
, "application/xml");
3514 RGWCORSConfiguration_S3
*s3cors
=
3515 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
3524 int RGWPutCORS_ObjStore_S3::get_params()
3526 RGWCORSXMLParser_S3
parser(s
->cct
);
3527 RGWCORSConfiguration_S3
*cors_config
;
3529 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
3533 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
3538 r
= do_aws4_auth_completion();
3543 if (!parser
.init()) {
3547 char* buf
= data
.c_str();
3548 if (!buf
|| !parser
.parse(buf
, data
.length(), 1)) {
3549 return -ERR_MALFORMED_XML
;
3552 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
3553 "CORSConfiguration"));
3555 return -ERR_MALFORMED_XML
;
3558 #define CORS_RULES_MAX_NUM 100
3559 int max_num
= s
->cct
->_conf
->rgw_cors_rules_max_num
;
3561 max_num
= CORS_RULES_MAX_NUM
;
3563 int cors_rules_num
= cors_config
->get_rules().size();
3564 if (cors_rules_num
> max_num
) {
3565 ldpp_dout(this, 4) << "An cors config can have up to "
3567 << " rules, request cors rules num: "
3568 << cors_rules_num
<< dendl
;
3569 op_ret
= -ERR_INVALID_CORS_RULES_ERROR
;
3570 s
->err
.message
= "The number of CORS rules should not exceed allowed limit of "
3571 + std::to_string(max_num
) + " rules.";
3572 return -ERR_INVALID_REQUEST
;
3575 // forward bucket cors requests to meta master zone
3576 if (!store
->svc()->zone
->is_meta_master()) {
3577 /* only need to keep this data around if we're not meta master */
3578 in_data
.append(data
);
3581 if (s
->cct
->_conf
->subsys
.should_gather
<ceph_subsys_rgw
, 15>()) {
3582 ldpp_dout(this, 15) << "CORSConfiguration";
3583 cors_config
->to_xml(*_dout
);
3587 cors_config
->encode(cors_bl
);
3592 void RGWPutCORS_ObjStore_S3::send_response()
3595 set_req_state_err(s
, op_ret
);
3597 end_header(s
, NULL
, "application/xml");
3601 void RGWDeleteCORS_ObjStore_S3::send_response()
3604 if (!r
|| r
== -ENOENT
)
3605 r
= STATUS_NO_CONTENT
;
3607 set_req_state_err(s
, r
);
3609 end_header(s
, NULL
);
3612 void RGWOptionsCORS_ObjStore_S3::send_response()
3614 string hdrs
, exp_hdrs
;
3615 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
3616 /*EACCES means, there is no CORS registered yet for the bucket
3617 *ENOENT means, there is no match of the Origin in the list of CORSRule
3619 if (op_ret
== -ENOENT
)
3622 set_req_state_err(s
, op_ret
);
3624 end_header(s
, NULL
);
3627 get_response_params(hdrs
, exp_hdrs
, &max_age
);
3630 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
3632 end_header(s
, NULL
);
3635 void RGWGetRequestPayment_ObjStore_S3::send_response()
3638 end_header(s
, this, "application/xml");
3641 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
3642 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
3643 s
->formatter
->dump_string("Payer", payer
);
3644 s
->formatter
->close_section();
3645 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3648 class RGWSetRequestPaymentParser
: public RGWXMLParser
3650 XMLObj
*alloc_obj(const char *el
) override
{
3655 RGWSetRequestPaymentParser() {}
3656 ~RGWSetRequestPaymentParser() override
{}
3658 int get_request_payment_payer(bool *requester_pays
) {
3659 XMLObj
*config
= find_first("RequestPaymentConfiguration");
3663 *requester_pays
= false;
3665 XMLObj
*field
= config
->find_first("Payer");
3669 auto& s
= field
->get_data();
3671 if (stringcasecmp(s
, "Requester") == 0) {
3672 *requester_pays
= true;
3673 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
3681 int RGWSetRequestPayment_ObjStore_S3::get_params()
3683 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
3686 std::tie(r
, in_data
) = rgw_rest_read_all_input(s
, max_size
, false);
3693 RGWSetRequestPaymentParser parser
;
3695 if (!parser
.init()) {
3696 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl
;
3700 char* buf
= in_data
.c_str();
3701 if (!parser
.parse(buf
, in_data
.length(), 1)) {
3702 ldpp_dout(this, 10) << "failed to parse data: " << buf
<< dendl
;
3706 return parser
.get_request_payment_payer(&requester_pays
);
3709 void RGWSetRequestPayment_ObjStore_S3::send_response()
3712 set_req_state_err(s
, op_ret
);
3717 int RGWInitMultipart_ObjStore_S3::get_params()
3719 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
3720 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
3729 void RGWInitMultipart_ObjStore_S3::send_response()
3732 set_req_state_err(s
, op_ret
);
3734 for (auto &it
: crypt_http_responses
)
3735 dump_header(s
, it
.first
, it
.second
);
3736 ceph::real_time abort_date
;
3738 bool exist_multipart_abort
= get_s3_multipart_abort_header(s
, mtime
, abort_date
, rule_id
);
3739 if (exist_multipart_abort
) {
3740 dump_time_header(s
, "x-amz-abort-date", abort_date
);
3741 dump_header_if_nonempty(s
, "x-amz-abort-rule-id", rule_id
);
3743 end_header(s
, this, "application/xml");
3746 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
3747 if (!s
->bucket_tenant
.empty())
3748 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
3749 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
3750 s
->formatter
->dump_string("Key", s
->object
.name
);
3751 s
->formatter
->dump_string("UploadId", upload_id
);
3752 s
->formatter
->close_section();
3753 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3757 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
3760 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
3764 int RGWCompleteMultipart_ObjStore_S3::get_params()
3766 int ret
= RGWCompleteMultipart_ObjStore::get_params();
3773 return do_aws4_auth_completion();
3776 void RGWCompleteMultipart_ObjStore_S3::send_response()
3779 set_req_state_err(s
, op_ret
);
3781 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
3782 end_header(s
, this, "application/xml");
3785 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
3786 std::string base_uri
= compute_domain_uri(s
);
3787 if (!s
->bucket_tenant
.empty()) {
3788 s
->formatter
->dump_format("Location", "%s/%s:%s/%s",
3790 s
->bucket_tenant
.c_str(),
3791 s
->bucket_name
.c_str(),
3792 s
->object
.name
.c_str()
3794 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
3796 s
->formatter
->dump_format("Location", "%s/%s/%s",
3798 s
->bucket_name
.c_str(),
3799 s
->object
.name
.c_str()
3802 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
3803 s
->formatter
->dump_string("Key", s
->object
.name
);
3804 s
->formatter
->dump_string("ETag", etag
);
3805 s
->formatter
->close_section();
3806 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3810 void RGWAbortMultipart_ObjStore_S3::send_response()
3814 r
= STATUS_NO_CONTENT
;
3816 set_req_state_err(s
, r
);
3818 end_header(s
, this);
3821 void RGWListMultipart_ObjStore_S3::send_response()
3824 set_req_state_err(s
, op_ret
);
3826 // Explicitly use chunked transfer encoding so that we can stream the result
3827 // to the user without having to wait for the full length of it.
3828 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
3832 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
3833 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
3834 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
3837 iter
= parts
.begin();
3838 test_iter
= parts
.rbegin();
3839 if (test_iter
!= parts
.rend()) {
3840 cur_max
= test_iter
->first
;
3842 if (!s
->bucket_tenant
.empty())
3843 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
3844 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
3845 s
->formatter
->dump_string("Key", s
->object
.name
);
3846 s
->formatter
->dump_string("UploadId", upload_id
);
3847 s
->formatter
->dump_string("StorageClass", "STANDARD");
3848 s
->formatter
->dump_int("PartNumberMarker", marker
);
3849 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
3850 s
->formatter
->dump_int("MaxParts", max_parts
);
3851 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
3853 ACLOwner
& owner
= policy
.get_owner();
3854 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
3856 for (; iter
!= parts
.end(); ++iter
) {
3857 RGWUploadPartInfo
& info
= iter
->second
;
3859 s
->formatter
->open_object_section("Part");
3861 dump_time(s
, "LastModified", &info
.modified
);
3863 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
3864 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
3865 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
3866 s
->formatter
->close_section();
3868 s
->formatter
->close_section();
3869 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3873 void RGWListBucketMultiparts_ObjStore_S3::send_response()
3876 set_req_state_err(s
, op_ret
);
3879 // Explicitly use chunked transfer encoding so that we can stream the result
3880 // to the user without having to wait for the full length of it.
3881 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
3886 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
3887 if (!s
->bucket_tenant
.empty())
3888 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
3889 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
3890 if (!prefix
.empty())
3891 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
3892 const string
& key_marker
= marker
.get_key();
3893 if (!key_marker
.empty())
3894 s
->formatter
->dump_string("KeyMarker", key_marker
);
3895 const string
& upload_id_marker
= marker
.get_upload_id();
3896 if (!upload_id_marker
.empty())
3897 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
3898 string next_key
= next_marker
.mp
.get_key();
3899 if (!next_key
.empty())
3900 s
->formatter
->dump_string("NextKeyMarker", next_key
);
3901 string next_upload_id
= next_marker
.mp
.get_upload_id();
3902 if (!next_upload_id
.empty())
3903 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
3904 s
->formatter
->dump_int("MaxUploads", max_uploads
);
3905 if (!delimiter
.empty())
3906 s
->formatter
->dump_string("Delimiter", delimiter
);
3907 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
3910 vector
<RGWMultipartUploadEntry
>::iterator iter
;
3911 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
3912 RGWMPObj
& mp
= iter
->mp
;
3913 s
->formatter
->open_array_section("Upload");
3915 s
->formatter
->dump_string("Key", url_encode(mp
.get_key(), false));
3917 s
->formatter
->dump_string("Key", mp
.get_key());
3919 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
3920 dump_owner(s
, s
->user
->get_id(), s
->user
->get_display_name(), "Initiator");
3921 dump_owner(s
, s
->user
->get_id(), s
->user
->get_display_name());
3922 s
->formatter
->dump_string("StorageClass", "STANDARD");
3923 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
3924 s
->formatter
->close_section();
3926 if (!common_prefixes
.empty()) {
3927 s
->formatter
->open_array_section("CommonPrefixes");
3928 for (const auto& kv
: common_prefixes
) {
3930 s
->formatter
->dump_string("CommonPrefixes.Prefix",
3931 url_encode(kv
.first
, false));
3933 s
->formatter
->dump_string("CommonPrefixes.Prefix", kv
.first
);
3936 s
->formatter
->close_section();
3939 s
->formatter
->close_section();
3940 rgw_flush_formatter_and_reset(s
, s
->formatter
);
3943 int RGWDeleteMultiObj_ObjStore_S3::get_params()
3945 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
3950 return do_aws4_auth_completion();
3953 void RGWDeleteMultiObj_ObjStore_S3::send_status()
3955 if (! status_dumped
) {
3957 set_req_state_err(s
, op_ret
);
3959 status_dumped
= true;
3963 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
3966 if (!status_dumped
) {
3971 // Explicitly use chunked transfer encoding so that we can stream the result
3972 // to the user without having to wait for the full length of it.
3973 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
3974 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
3976 rgw_flush_formatter(s
, s
->formatter
);
3979 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
3981 const string
& marker_version_id
, int ret
)
3984 if (ret
== 0 && !quiet
) {
3985 s
->formatter
->open_object_section("Deleted");
3986 s
->formatter
->dump_string("Key", key
.name
);
3987 if (!key
.instance
.empty()) {
3988 s
->formatter
->dump_string("VersionId", key
.instance
);
3990 if (delete_marker
) {
3991 s
->formatter
->dump_bool("DeleteMarker", true);
3992 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
3994 s
->formatter
->close_section();
3995 } else if (ret
< 0) {
3996 struct rgw_http_error r
;
3999 s
->formatter
->open_object_section("Error");
4002 rgw_get_errno_s3(&r
, err_no
);
4004 s
->formatter
->dump_string("Key", key
.name
);
4005 s
->formatter
->dump_string("VersionId", key
.instance
);
4006 s
->formatter
->dump_string("Code", r
.s3_code
);
4007 s
->formatter
->dump_string("Message", r
.s3_code
);
4008 s
->formatter
->close_section();
4011 rgw_flush_formatter(s
, s
->formatter
);
4015 void RGWDeleteMultiObj_ObjStore_S3::end_response()
4018 s
->formatter
->close_section();
4019 rgw_flush_formatter_and_reset(s
, s
->formatter
);
4022 void RGWGetObjLayout_ObjStore_S3::send_response()
4025 set_req_state_err(s
, op_ret
);
4027 end_header(s
, this, "application/json");
4035 f
.open_object_section("result");
4036 ::encode_json("head", head_obj
, &f
);
4037 ::encode_json("manifest", *manifest
, &f
);
4038 f
.open_array_section("data_location");
4039 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
4040 f
.open_object_section("obj");
4041 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
->getRados());
4042 uint64_t ofs
= miter
.get_ofs();
4043 uint64_t left
= manifest
->get_obj_size() - ofs
;
4044 ::encode_json("ofs", miter
.get_ofs(), &f
);
4045 ::encode_json("loc", raw_loc
, &f
);
4046 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
4047 uint64_t loc_size
= miter
.get_stripe_size();
4048 if (loc_size
> left
) {
4051 ::encode_json("loc_size", loc_size
, &f
);
4053 rgw_flush_formatter(s
, &f
);
4057 rgw_flush_formatter(s
, &f
);
4060 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
4062 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
4063 if (iter
== s
->info
.x_meta_map
.end()) {
4064 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
4065 ldpp_dout(this, 5) << s
->err
.message
<< dendl
;
4069 list
<string
> expressions
;
4070 get_str_list(iter
->second
, ",", expressions
);
4072 for (auto& expression
: expressions
) {
4073 vector
<string
> args
;
4074 get_str_vec(expression
, ";", args
);
4077 s
->err
.message
= "invalid empty expression";
4078 ldpp_dout(this, 5) << s
->err
.message
<< dendl
;
4081 if (args
.size() > 2) {
4082 s
->err
.message
= string("invalid expression: ") + expression
;
4083 ldpp_dout(this, 5) << s
->err
.message
<< dendl
;
4087 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
4089 if (args
.size() > 1) {
4090 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
4093 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
4094 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
4095 ldpp_dout(this, 5) << s
->err
.message
<< dendl
;
4099 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
4101 ESEntityTypeMap::EntityType entity_type
;
4103 if (val
.empty() || val
== "str" || val
== "string") {
4104 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
4105 } else if (val
== "int" || val
== "integer") {
4106 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
4107 } else if (val
== "date" || val
== "datetime") {
4108 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
4110 s
->err
.message
= string("invalid entity type: ") + val
;
4111 ldpp_dout(this, 5) << s
->err
.message
<< dendl
;
4115 mdsearch_config
[key
] = entity_type
;
4121 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
4124 set_req_state_err(s
, op_ret
);
4126 end_header(s
, this);
4129 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
4132 set_req_state_err(s
, op_ret
);
4134 end_header(s
, NULL
, "application/xml");
4136 Formatter
*f
= s
->formatter
;
4137 f
->open_array_section("GetBucketMetaSearchResult");
4138 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
4139 f
->open_object_section("Entry");
4140 string k
= string("x-amz-meta-") + e
.first
;
4141 f
->dump_string("Key", k
.c_str());
4144 case ESEntityTypeMap::ES_ENTITY_INT
:
4147 case ESEntityTypeMap::ES_ENTITY_DATE
:
4153 f
->dump_string("Type", type
);
4157 rgw_flush_formatter(s
, f
);
4160 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
4163 set_req_state_err(s
, op_ret
);
4165 end_header(s
, this);
4168 void RGWPutBucketObjectLock_ObjStore_S3::send_response()
4171 set_req_state_err(s
, op_ret
);
4177 void RGWGetBucketObjectLock_ObjStore_S3::send_response()
4180 set_req_state_err(s
, op_ret
);
4183 end_header(s
, this, "application/xml");
4189 encode_xml("ObjectLockConfiguration", s
->bucket_info
.obj_lock
, s
->formatter
);
4190 rgw_flush_formatter_and_reset(s
, s
->formatter
);
4194 int RGWPutObjRetention_ObjStore_S3::get_params()
4196 const char *bypass_gov_header
= s
->info
.env
->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
4197 if (bypass_gov_header
) {
4198 std::string bypass_gov_decoded
= url_decode(bypass_gov_header
);
4199 bypass_governance_mode
= boost::algorithm::iequals(bypass_gov_decoded
, "true");
4202 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
4203 std::tie(op_ret
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
4207 void RGWPutObjRetention_ObjStore_S3::send_response()
4210 set_req_state_err(s
, op_ret
);
4216 void RGWGetObjRetention_ObjStore_S3::send_response()
4219 set_req_state_err(s
, op_ret
);
4222 end_header(s
, this, "application/xml");
4228 encode_xml("Retention", obj_retention
, s
->formatter
);
4229 rgw_flush_formatter_and_reset(s
, s
->formatter
);
4232 void RGWPutObjLegalHold_ObjStore_S3::send_response()
4235 set_req_state_err(s
, op_ret
);
4241 void RGWGetObjLegalHold_ObjStore_S3::send_response()
4244 set_req_state_err(s
, op_ret
);
4247 end_header(s
, this, "application/xml");
4253 encode_xml("LegalHold", obj_legal_hold
, s
->formatter
);
4254 rgw_flush_formatter_and_reset(s
, s
->formatter
);
4257 void RGWGetBucketPolicyStatus_ObjStore_S3::send_response()
4260 set_req_state_err(s
, op_ret
);
4263 end_header(s
, this, "application/xml");
4266 s
->formatter
->open_object_section_in_ns("PolicyStatus", XMLNS_AWS_S3
);
4267 // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETPolicyStatus.html
4268 // mentions TRUE and FALSE, but boto/aws official clients seem to want lower
4269 // case which is returned by AWS as well; so let's be bug to bug compatible
4271 s
->formatter
->dump_bool("IsPublic", isPublic
);
4272 s
->formatter
->close_section();
4273 rgw_flush_formatter_and_reset(s
, s
->formatter
);
4277 void RGWPutBucketPublicAccessBlock_ObjStore_S3::send_response()
4280 set_req_state_err(s
, op_ret
);
4286 void RGWGetBucketPublicAccessBlock_ObjStore_S3::send_response()
4289 set_req_state_err(s
, op_ret
);
4292 end_header(s
, this, "application/xml");
4295 access_conf
.dump_xml(s
->formatter
);
4296 rgw_flush_formatter_and_reset(s
, s
->formatter
);
4299 RGWOp
*RGWHandler_REST_Service_S3::op_get()
4301 if (is_usage_op()) {
4302 return new RGWGetUsage_ObjStore_S3
;
4304 return new RGWListBuckets_ObjStore_S3
;
4308 RGWOp
*RGWHandler_REST_Service_S3::op_head()
4310 return new RGWListBuckets_ObjStore_S3
;
4313 RGWOp
*RGWHandler_REST_Service_S3::op_post()
4315 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
4319 std::tie(ret
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
4324 const auto post_body
= data
.to_str();
4327 RGWHandler_REST_STS
sts_handler(auth_registry
, post_body
);
4328 sts_handler
.init(store
, s
, s
->cio
);
4329 auto op
= sts_handler
.get_op();
4336 RGWHandler_REST_IAM
iam_handler(auth_registry
, post_body
);
4337 iam_handler
.init(store
, s
, s
->cio
);
4338 auto op
= iam_handler
.get_op();
4345 RGWHandler_REST_PSTopic_AWS
topic_handler(auth_registry
, post_body
);
4346 topic_handler
.init(store
, s
, s
->cio
);
4347 auto op
= topic_handler
.get_op();
4356 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
) const
4361 s
->info
.args
.get_int("list-type", &list_type
, 1);
4362 switch (list_type
) {
4364 return new RGWListBucket_ObjStore_S3
;
4366 return new RGWListBucket_ObjStore_S3v2
;
4368 ldpp_dout(s
, 5) << __func__
<< ": unsupported list-type " << list_type
<< dendl
;
4369 return new RGWListBucket_ObjStore_S3
;
4372 return new RGWStatBucket_ObjStore_S3
;
4376 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
4378 if (s
->info
.args
.sub_resource_exists("encryption"))
4381 if (s
->info
.args
.sub_resource_exists("logging"))
4382 return new RGWGetBucketLogging_ObjStore_S3
;
4384 if (s
->info
.args
.sub_resource_exists("location"))
4385 return new RGWGetBucketLocation_ObjStore_S3
;
4387 if (s
->info
.args
.sub_resource_exists("versioning"))
4388 return new RGWGetBucketVersioning_ObjStore_S3
;
4390 if (s
->info
.args
.sub_resource_exists("website")) {
4391 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
4394 return new RGWGetBucketWebsite_ObjStore_S3
;
4397 if (s
->info
.args
.exists("mdsearch")) {
4398 return new RGWGetBucketMetaSearch_ObjStore_S3
;
4402 return new RGWGetACLs_ObjStore_S3
;
4403 } else if (is_cors_op()) {
4404 return new RGWGetCORS_ObjStore_S3
;
4405 } else if (is_request_payment_op()) {
4406 return new RGWGetRequestPayment_ObjStore_S3
;
4407 } else if (s
->info
.args
.exists("uploads")) {
4408 return new RGWListBucketMultiparts_ObjStore_S3
;
4409 } else if(is_lc_op()) {
4410 return new RGWGetLC_ObjStore_S3
;
4411 } else if(is_policy_op()) {
4412 return new RGWGetBucketPolicy
;
4413 } else if (is_tagging_op()) {
4414 return new RGWGetBucketTags_ObjStore_S3
;
4415 } else if (is_object_lock_op()) {
4416 return new RGWGetBucketObjectLock_ObjStore_S3
;
4417 } else if (is_notification_op()) {
4418 return RGWHandler_REST_PSNotifs_S3::create_get_op();
4419 } else if (is_replication_op()) {
4420 return new RGWGetBucketReplication_ObjStore_S3
;
4421 } else if (is_policy_status_op()) {
4422 return new RGWGetBucketPolicyStatus_ObjStore_S3
;
4423 } else if (is_block_public_access_op()) {
4424 return new RGWGetBucketPublicAccessBlock_ObjStore_S3
;
4426 return get_obj_op(true);
4429 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
4432 return new RGWGetACLs_ObjStore_S3
;
4433 } else if (s
->info
.args
.exists("uploads")) {
4434 return new RGWListBucketMultiparts_ObjStore_S3
;
4436 return get_obj_op(false);
4439 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
4441 if (s
->info
.args
.sub_resource_exists("logging") ||
4442 s
->info
.args
.sub_resource_exists("encryption"))
4444 if (s
->info
.args
.sub_resource_exists("versioning"))
4445 return new RGWSetBucketVersioning_ObjStore_S3
;
4446 if (s
->info
.args
.sub_resource_exists("website")) {
4447 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
4450 return new RGWSetBucketWebsite_ObjStore_S3
;
4452 if (is_tagging_op()) {
4453 return new RGWPutBucketTags_ObjStore_S3
;
4454 } else if (is_acl_op()) {
4455 return new RGWPutACLs_ObjStore_S3
;
4456 } else if (is_cors_op()) {
4457 return new RGWPutCORS_ObjStore_S3
;
4458 } else if (is_request_payment_op()) {
4459 return new RGWSetRequestPayment_ObjStore_S3
;
4460 } else if(is_lc_op()) {
4461 return new RGWPutLC_ObjStore_S3
;
4462 } else if(is_policy_op()) {
4463 return new RGWPutBucketPolicy
;
4464 } else if (is_object_lock_op()) {
4465 return new RGWPutBucketObjectLock_ObjStore_S3
;
4466 } else if (is_notification_op()) {
4467 return RGWHandler_REST_PSNotifs_S3::create_put_op();
4468 } else if (is_replication_op()) {
4469 auto sync_policy_handler
= store
->svc()->zone
->get_sync_policy_handler(nullopt
);
4470 if (!sync_policy_handler
||
4471 sync_policy_handler
->is_legacy_config()) {
4475 return new RGWPutBucketReplication_ObjStore_S3
;
4476 } else if (is_block_public_access_op()) {
4477 return new RGWPutBucketPublicAccessBlock_ObjStore_S3
;
4479 return new RGWCreateBucket_ObjStore_S3
;
4482 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
4484 if (s
->info
.args
.sub_resource_exists("logging") ||
4485 s
->info
.args
.sub_resource_exists("encryption"))
4488 if (is_tagging_op()) {
4489 return new RGWDeleteBucketTags_ObjStore_S3
;
4490 } else if (is_cors_op()) {
4491 return new RGWDeleteCORS_ObjStore_S3
;
4492 } else if(is_lc_op()) {
4493 return new RGWDeleteLC_ObjStore_S3
;
4494 } else if(is_policy_op()) {
4495 return new RGWDeleteBucketPolicy
;
4496 } else if (is_notification_op()) {
4497 return RGWHandler_REST_PSNotifs_S3::create_delete_op();
4498 } else if (is_replication_op()) {
4499 return new RGWDeleteBucketReplication_ObjStore_S3
;
4500 } else if (is_block_public_access_op()) {
4501 return new RGWDeleteBucketPublicAccessBlock
;
4504 if (s
->info
.args
.sub_resource_exists("website")) {
4505 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
4508 return new RGWDeleteBucketWebsite_ObjStore_S3
;
4511 if (s
->info
.args
.exists("mdsearch")) {
4512 return new RGWDelBucketMetaSearch_ObjStore_S3
;
4515 return new RGWDeleteBucket_ObjStore_S3
;
4518 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
4520 if (s
->info
.args
.exists("delete")) {
4521 return new RGWDeleteMultiObj_ObjStore_S3
;
4524 if (s
->info
.args
.exists("mdsearch")) {
4525 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
4528 return new RGWPostObj_ObjStore_S3
;
4531 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
4533 return new RGWOptionsCORS_ObjStore_S3
;
4536 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
4538 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
4539 get_obj_op
->set_get_data(get_data
);
4543 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
4546 return new RGWGetACLs_ObjStore_S3
;
4547 } else if (s
->info
.args
.exists("uploadId")) {
4548 return new RGWListMultipart_ObjStore_S3
;
4549 } else if (s
->info
.args
.exists("layout")) {
4550 return new RGWGetObjLayout_ObjStore_S3
;
4551 } else if (is_tagging_op()) {
4552 return new RGWGetObjTags_ObjStore_S3
;
4553 } else if (is_obj_retention_op()) {
4554 return new RGWGetObjRetention_ObjStore_S3
;
4555 } else if (is_obj_legal_hold_op()) {
4556 return new RGWGetObjLegalHold_ObjStore_S3
;
4558 return get_obj_op(true);
4561 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
4564 return new RGWGetACLs_ObjStore_S3
;
4565 } else if (s
->info
.args
.exists("uploadId")) {
4566 return new RGWListMultipart_ObjStore_S3
;
4568 return get_obj_op(false);
4571 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
4574 return new RGWPutACLs_ObjStore_S3
;
4575 } else if (is_tagging_op()) {
4576 return new RGWPutObjTags_ObjStore_S3
;
4577 } else if (is_obj_retention_op()) {
4578 return new RGWPutObjRetention_ObjStore_S3
;
4579 } else if (is_obj_legal_hold_op()) {
4580 return new RGWPutObjLegalHold_ObjStore_S3
;
4583 if (s
->init_state
.src_bucket
.empty())
4584 return new RGWPutObj_ObjStore_S3
;
4586 return new RGWCopyObj_ObjStore_S3
;
4589 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
4591 if (is_tagging_op()) {
4592 return new RGWDeleteObjTags_ObjStore_S3
;
4594 string upload_id
= s
->info
.args
.get("uploadId");
4596 if (upload_id
.empty())
4597 return new RGWDeleteObj_ObjStore_S3
;
4599 return new RGWAbortMultipart_ObjStore_S3
;
4602 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
4604 if (s
->info
.args
.exists("uploadId"))
4605 return new RGWCompleteMultipart_ObjStore_S3
;
4607 if (s
->info
.args
.exists("uploads"))
4608 return new RGWInitMultipart_ObjStore_S3
;
4610 return new RGWPostObj_ObjStore_S3
;
4613 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
4615 return new RGWOptionsCORS_ObjStore_S3
;
4618 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
4619 int default_formatter
,
4620 bool configurable_format
)
4625 const char *req_name
= s
->relative_uri
.c_str();
4628 if (*req_name
== '?') {
4631 p
= s
->info
.request_params
.c_str();
4634 s
->info
.args
.set(p
);
4635 s
->info
.args
.parse();
4637 /* must be called after the args parsing */
4638 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
4642 if (*req_name
!= '/')
4651 int pos
= req
.find('/');
4653 first
= req
.substr(0, pos
);
4659 * XXX The intent of the check for empty is apparently to let the bucket
4660 * name from DNS to be set ahead. However, we currently take the DNS
4661 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
4662 * So, this check is meaningless.
4664 * Rather than dropping this, the code needs to be changed into putting
4665 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
4666 * into req_status.bucket_name directly.
4668 if (s
->init_state
.url_bucket
.empty()) {
4669 // Save bucket to tide us over until token is parsed.
4670 s
->init_state
.url_bucket
= first
;
4672 string encoded_obj_str
= req
.substr(pos
+1);
4673 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
4676 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
4681 static int verify_mfa(rgw::sal::RGWRadosStore
*store
, RGWUserInfo
*user
, const string
& mfa_str
, bool *verified
, const DoutPrefixProvider
*dpp
)
4683 vector
<string
> params
;
4684 get_str_vec(mfa_str
, " ", params
);
4686 if (params
.size() != 2) {
4687 ldpp_dout(dpp
, 5) << "NOTICE: invalid mfa string provided: " << mfa_str
<< dendl
;
4691 string
& serial
= params
[0];
4692 string
& pin
= params
[1];
4694 auto i
= user
->mfa_ids
.find(serial
);
4695 if (i
== user
->mfa_ids
.end()) {
4696 ldpp_dout(dpp
, 5) << "NOTICE: user does not have mfa device with serial=" << serial
<< dendl
;
4700 int ret
= store
->svc()->cls
->mfa
.check_mfa(user
->user_id
, serial
, pin
, null_yield
);
4702 ldpp_dout(dpp
, 20) << "NOTICE: failed to check MFA, serial=" << serial
<< dendl
;
4711 int RGWHandler_REST_S3::postauth_init()
4713 struct req_init_state
*t
= &s
->init_state
;
4715 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->get_tenant(),
4716 s
->bucket_tenant
, s
->bucket_name
);
4718 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
4719 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
4722 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
4725 if (!s
->bucket_name
.empty()) {
4726 ret
= validate_object_name(s
->object
.name
);
4731 if (!t
->src_bucket
.empty()) {
4732 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->get_tenant(),
4733 s
->src_tenant_name
, s
->src_bucket_name
);
4734 ret
= rgw_validate_tenant_name(s
->src_tenant_name
);
4739 const char *mfa
= s
->info
.env
->get("HTTP_X_AMZ_MFA");
4741 ret
= verify_mfa(store
, &s
->user
->get_info(), string(mfa
), &s
->mfa_verified
, s
);
4747 int RGWHandler_REST_S3::init(rgw::sal::RGWRadosStore
*store
, struct req_state
*s
,
4748 rgw::io::BasicClient
*cio
)
4754 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
4757 if (!s
->bucket_name
.empty()) {
4758 ret
= validate_object_name(s
->object
.name
);
4763 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
4765 s
->canned_acl
= cacl
;
4767 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
4769 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
4771 (! s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) &&
4772 (! s
->info
.args
.exists("uploadId"))) {
4774 ret
= RGWCopyObj::parse_copy_location(copy_source
,
4775 s
->init_state
.src_bucket
,
4778 ldpp_dout(s
, 0) << "failed to parse copy location" << dendl
;
4779 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
4783 const char *sc
= s
->info
.env
->get("HTTP_X_AMZ_STORAGE_CLASS");
4785 s
->info
.storage_class
= sc
;
4788 return RGWHandler_REST::init(store
, s
, cio
);
4791 int RGWHandler_REST_S3::authorize(const DoutPrefixProvider
*dpp
)
4793 if (s
->info
.args
.exists("Action") && s
->info
.args
.get("Action") == "AssumeRoleWithWebIdentity") {
4794 return RGW_Auth_STS::authorize(dpp
, store
, auth_registry
, s
);
4796 return RGW_Auth_S3::authorize(dpp
, store
, auth_registry
, s
);
4799 enum class AwsVersion
{
4805 enum class AwsRoute
{
4811 static inline std::pair
<AwsVersion
, AwsRoute
>
4812 discover_aws_flavour(const req_info
& info
)
4814 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
4816 AwsVersion version
= AwsVersion::UNKNOWN
;
4817 AwsRoute route
= AwsRoute::UNKNOWN
;
4819 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
4820 if (http_auth
&& http_auth
[0]) {
4821 /* Authorization in Header */
4822 route
= AwsRoute::HEADERS
;
4824 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
4825 strlen(AWS4_HMAC_SHA256_STR
))) {
4827 version
= AwsVersion::V4
;
4828 } else if (!strncmp(http_auth
, "AWS ", 4)) {
4830 version
= AwsVersion::V2
;
4833 route
= AwsRoute::QUERY_STRING
;
4835 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
4837 version
= AwsVersion::V4
;
4838 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
4840 version
= AwsVersion::V2
;
4844 return std::make_pair(version
, route
);
4848 * verify that a signed request comes from the keyholder
4849 * by checking the signature against our locally-computed version
4851 * it tries AWS v4 before AWS v2
4853 int RGW_Auth_S3::authorize(const DoutPrefixProvider
*dpp
,
4854 rgw::sal::RGWRadosStore
* const store
,
4855 const rgw::auth::StrategyRegistry
& auth_registry
,
4856 struct req_state
* const s
)
4859 /* neither keystone and rados enabled; warn and exit! */
4860 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
4861 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
4862 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
4863 ldpp_dout(dpp
, 0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
4867 const auto ret
= rgw::auth::Strategy::apply(dpp
, auth_registry
.get_s3_main(), s
);
4869 /* Populate the owner info. */
4870 s
->owner
.set_id(s
->user
->get_id());
4871 s
->owner
.set_name(s
->user
->get_display_name());
4876 int RGWHandler_Auth_S3::init(rgw::sal::RGWRadosStore
*store
, struct req_state
*state
,
4877 rgw::io::BasicClient
*cio
)
4879 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
4884 return RGWHandler_REST::init(store
, state
, cio
);
4887 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
4888 const rgw::auth::StrategyRegistry
& auth_registry
,
4889 const std::string
& frontend_prefix
)
4891 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
4893 RGWHandler_REST_S3::init_from_header(s
,
4894 is_s3website
? RGW_FORMAT_HTML
:
4895 RGW_FORMAT_XML
, true);
4899 RGWHandler_REST
* handler
;
4900 // TODO: Make this more readable
4902 if (s
->init_state
.url_bucket
.empty()) {
4903 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
4904 } else if (s
->object
.empty()) {
4905 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
4907 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
4910 if (s
->init_state
.url_bucket
.empty()) {
4911 handler
= new RGWHandler_REST_Service_S3(auth_registry
, enable_sts
, enable_iam
, enable_pubsub
);
4912 } else if (s
->object
.empty()) {
4913 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
, enable_pubsub
);
4915 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
4919 ldpp_dout(s
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
4924 bool RGWHandler_REST_S3Website::web_dir() const {
4925 std::string subdir_name
= url_decode(s
->object
.name
);
4927 if (subdir_name
.empty()) {
4929 } else if (subdir_name
.back() == '/' && subdir_name
.size() > 1) {
4930 subdir_name
.pop_back();
4933 rgw_obj
obj(s
->bucket
, subdir_name
);
4935 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
4936 obj_ctx
.set_atomic(obj
);
4937 obj_ctx
.set_prefetch_data(obj
);
4939 RGWObjState
* state
= nullptr;
4940 if (store
->getRados()->get_obj_state(&obj_ctx
, s
->bucket_info
, obj
, &state
, false, s
->yield
) < 0) {
4943 if (! state
->exists
) {
4946 return state
->exists
;
4949 int RGWHandler_REST_S3Website::init(rgw::sal::RGWRadosStore
*store
, req_state
*s
,
4950 rgw::io::BasicClient
* cio
)
4952 // save the original object name before retarget() replaces it with the
4953 // result of get_effective_key(). the error_handler() needs the original
4954 // object name for redirect handling
4955 original_object_name
= s
->object
.name
;
4957 return RGWHandler_REST_S3::init(store
, s
, cio
);
4960 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
4962 ldpp_dout(s
, 10) << __func__
<< " Starting retarget" << dendl
;
4964 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
4967 int ret
= store
->getRados()->get_bucket_info(store
->svc(), s
->bucket_tenant
,
4968 s
->bucket_name
, s
->bucket_info
, NULL
,
4969 s
->yield
, &s
->bucket_attrs
);
4971 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
4972 return -ERR_NO_SUCH_BUCKET
;
4974 if (!s
->bucket_info
.has_website
) {
4975 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
4976 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
4979 rgw_obj_key new_obj
;
4980 bool get_res
= s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
, web_dir());
4982 s
->err
.message
= "The IndexDocument Suffix is not configurated or not well formed!";
4983 ldpp_dout(s
, 5) << s
->err
.message
<< dendl
;
4987 ldpp_dout(s
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
4988 << new_obj
<< dendl
;
4990 RGWBWRoutingRule rrule
;
4991 bool should_redirect
=
4992 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
4994 if (should_redirect
) {
4995 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
4996 const string
& protocol
=
4997 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
4998 int redirect_code
= 0;
4999 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
5001 // APply a custom HTTP response code
5002 if (redirect_code
> 0)
5003 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
5004 ldpp_dout(s
, 10) << "retarget redirect code=" << redirect_code
5005 << " proto+host:" << protocol
<< "://" << hostname
5006 << " -> " << s
->redirect
<< dendl
;
5007 return -ERR_WEBSITE_REDIRECT
;
5011 * FIXME: if s->object != new_obj, drop op and create a new op to handle
5012 * operation. Or remove this comment if it's not applicable anymore
5015 s
->object
= new_obj
;
5020 RGWOp
* RGWHandler_REST_S3Website::op_get()
5022 return get_obj_op(true);
5025 RGWOp
* RGWHandler_REST_S3Website::op_head()
5027 return get_obj_op(false);
5030 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
5032 s
->formatter
->reset(); /* Try to throw it all away */
5034 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
5035 if (getop
.get() == NULL
) {
5036 return -1; // Trigger double error handler
5038 getop
->init(store
, s
, this);
5039 getop
->range_str
= NULL
;
5040 getop
->if_mod
= NULL
;
5041 getop
->if_unmod
= NULL
;
5042 getop
->if_match
= NULL
;
5043 getop
->if_nomatch
= NULL
;
5044 s
->object
= errordoc_key
;
5046 ret
= init_permissions(getop
.get());
5048 ldpp_dout(s
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
5049 return -1; // Trigger double error handler
5052 ret
= read_permissions(getop
.get());
5054 ldpp_dout(s
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
5055 return -1; // Trigger double error handler
5059 getop
->set_custom_http_response(http_ret
);
5062 ret
= getop
->init_processing();
5064 ldpp_dout(s
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
5065 return -1; // Trigger double error handler
5068 ret
= getop
->verify_op_mask();
5070 ldpp_dout(s
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
5071 return -1; // Trigger double error handler
5074 ret
= getop
->verify_permission();
5076 ldpp_dout(s
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
5077 return -1; // Trigger double error handler
5080 ret
= getop
->verify_params();
5082 ldpp_dout(s
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
5083 return -1; // Trigger double error handler
5086 // No going back now
5089 * FIXME Missing headers:
5090 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
5091 * x-amz-error-code: NoSuchKey
5092 * x-amz-error-message: The specified key does not exist.
5093 * x-amz-error-detail-Key: foo
5101 int RGWHandler_REST_S3Website::error_handler(int err_no
,
5102 string
* error_content
) {
5103 int new_err_no
= -1;
5104 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
5105 int http_error_code
= -1;
5107 if (r
!= rgw_http_s3_errors
.end()) {
5108 http_error_code
= r
->second
.first
;
5110 ldpp_dout(s
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
5112 RGWBWRoutingRule rrule
;
5113 bool should_redirect
=
5114 s
->bucket_info
.website_conf
.should_redirect(original_object_name
,
5115 http_error_code
, &rrule
);
5117 if (should_redirect
) {
5118 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
5119 const string
& protocol
=
5120 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
5121 int redirect_code
= 0;
5122 rrule
.apply_rule(protocol
, hostname
, original_object_name
,
5123 &s
->redirect
, &redirect_code
);
5124 // Apply a custom HTTP response code
5125 if (redirect_code
> 0)
5126 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
5127 ldpp_dout(s
, 10) << "error handler redirect code=" << redirect_code
5128 << " proto+host:" << protocol
<< "://" << hostname
5129 << " -> " << s
->redirect
<< dendl
;
5130 return -ERR_WEBSITE_REDIRECT
;
5131 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
5132 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
5133 // Do NOT fire the ErrorDoc handler
5134 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
5135 /* This serves an entire page!
5136 On success, it will return zero, and no further content should be sent to the socket
5137 On failure, we need the double-error handler
5139 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
5140 if (new_err_no
!= -1) {
5141 err_no
= new_err_no
;
5144 ldpp_dout(s
, 20) << "No special error handling today!" << dendl
;
5150 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
5152 /** If we are in website mode, then it is explicitly impossible to run GET or
5153 * HEAD on the actual directory. We must convert the request to run on the
5154 * suffix object instead!
5156 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
5157 op
->set_get_data(get_data
);
5161 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
5163 /** If we are in website mode, then it is explicitly impossible to run GET or
5164 * HEAD on the actual directory. We must convert the request to run on the
5165 * suffix object instead!
5167 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
5168 op
->set_get_data(get_data
);
5172 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
5174 /** If we are in website mode, then it is explicitly impossible to run GET or
5175 * HEAD on the actual directory. We must convert the request to run on the
5176 * suffix object instead!
5178 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
5179 op
->set_get_data(get_data
);
5184 namespace rgw::auth::s3
{
5186 static rgw::auth::Completer::cmplptr_t
5187 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
5193 AWSEngine::VersionAbstractor::auth_data_t
5194 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
5198 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
5200 if (version
== AwsVersion::V2
) {
5201 return get_auth_data_v2(s
);
5202 } else if (version
== AwsVersion::V4
) {
5203 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
5205 /* FIXME(rzarzynski): handle anon user. */
5210 boost::optional
<std::string
>
5211 AWSGeneralAbstractor::get_v4_canonical_headers(
5212 const req_info
& info
,
5213 const boost::string_view
& signedheaders
,
5214 const bool using_qs
) const
5216 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
5220 AWSEngine::VersionAbstractor::auth_data_t
5221 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
5222 const bool using_qs
) const
5224 boost::string_view access_key_id
;
5225 boost::string_view signed_hdrs
;
5227 boost::string_view date
;
5228 boost::string_view credential_scope
;
5229 boost::string_view client_signature
;
5230 boost::string_view session_token
;
5232 int ret
= rgw::auth::s3::parse_v4_credentials(s
->info
,
5244 /* craft canonical headers */
5245 boost::optional
<std::string
> canonical_headers
= \
5246 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
5247 if (canonical_headers
) {
5248 using sanitize
= rgw::crypt_sanitize::log_content
;
5249 ldpp_dout(s
, 10) << "canonical headers format = "
5250 << sanitize
{*canonical_headers
} << dendl
;
5255 bool is_non_s3_op
= false;
5256 if (s
->op_type
== RGW_STS_GET_SESSION_TOKEN
||
5257 s
->op_type
== RGW_STS_ASSUME_ROLE
||
5258 s
->op_type
== RGW_STS_ASSUME_ROLE_WEB_IDENTITY
||
5259 s
->op_type
== RGW_OP_CREATE_ROLE
||
5260 s
->op_type
== RGW_OP_DELETE_ROLE
||
5261 s
->op_type
== RGW_OP_GET_ROLE
||
5262 s
->op_type
== RGW_OP_MODIFY_ROLE
||
5263 s
->op_type
== RGW_OP_LIST_ROLES
||
5264 s
->op_type
== RGW_OP_PUT_ROLE_POLICY
||
5265 s
->op_type
== RGW_OP_GET_ROLE_POLICY
||
5266 s
->op_type
== RGW_OP_LIST_ROLE_POLICIES
||
5267 s
->op_type
== RGW_OP_DELETE_ROLE_POLICY
||
5268 s
->op_type
== RGW_OP_PUT_USER_POLICY
||
5269 s
->op_type
== RGW_OP_GET_USER_POLICY
||
5270 s
->op_type
== RGW_OP_LIST_USER_POLICIES
||
5271 s
->op_type
== RGW_OP_DELETE_USER_POLICY
||
5272 s
->op_type
== RGW_OP_CREATE_OIDC_PROVIDER
||
5273 s
->op_type
== RGW_OP_DELETE_OIDC_PROVIDER
||
5274 s
->op_type
== RGW_OP_GET_OIDC_PROVIDER
||
5275 s
->op_type
== RGW_OP_LIST_OIDC_PROVIDERS
) {
5276 is_non_s3_op
= true;
5279 const char* exp_payload_hash
= nullptr;
5280 string payload_hash
;
5282 //For non s3 ops, we need to calculate the payload hash
5283 payload_hash
= s
->info
.args
.get("PayloadHash");
5284 exp_payload_hash
= payload_hash
.c_str();
5286 /* Get the expected hash. */
5287 exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
5290 /* Craft canonical URI. Using std::move later so let it be non-const. */
5291 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
5293 /* Craft canonical query string. std::moving later so non-const here. */
5294 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
5296 /* Craft canonical request. */
5297 auto canonical_req_hash
= \
5298 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
5300 std::move(canonical_uri
),
5301 std::move(canonical_qs
),
5302 std::move(*canonical_headers
),
5306 auto string_to_sign
= \
5307 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
5308 AWS4_HMAC_SHA256_STR
,
5311 std::move(canonical_req_hash
));
5313 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
5315 std::placeholders::_1
,
5316 std::placeholders::_2
,
5317 std::placeholders::_3
);
5319 /* Requests authenticated with the Query Parameters are treated as unsigned.
5320 * From "Authenticating Requests: Using Query Parameters (AWS Signature
5323 * You don't include a payload hash in the Canonical Request, because
5324 * when you create a presigned URL, you don't know the payload content
5325 * because the URL is used to upload an arbitrary payload. Instead, you
5326 * use a constant string UNSIGNED-PAYLOAD.
5328 * This means we have absolutely no business in spawning completer. Both
5329 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
5330 * by default. We don't need to change that. */
5331 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
) || is_non_s3_op
) {
5336 std::move(string_to_sign
),
5338 null_completer_factory
5341 /* We're going to handle a signed payload. Be aware that even empty HTTP
5342 * body (no payload) requires verification:
5344 * The x-amz-content-sha256 header is required for all AWS Signature
5345 * Version 4 requests. It provides a hash of the request payload. If
5346 * there is no payload, you must provide the hash of an empty string. */
5347 if (!is_v4_payload_streamed(exp_payload_hash
)) {
5348 ldpp_dout(s
, 10) << "delaying v4 auth" << dendl
;
5350 /* payload in a single chunk */
5353 case RGW_OP_CREATE_BUCKET
:
5354 case RGW_OP_PUT_OBJ
:
5355 case RGW_OP_PUT_ACLS
:
5356 case RGW_OP_PUT_CORS
:
5357 case RGW_OP_INIT_MULTIPART
: // in case that Init Multipart uses CHUNK encoding
5358 case RGW_OP_COMPLETE_MULTIPART
:
5359 case RGW_OP_SET_BUCKET_VERSIONING
:
5360 case RGW_OP_DELETE_MULTI_OBJ
:
5361 case RGW_OP_ADMIN_SET_METADATA
:
5362 case RGW_OP_SET_BUCKET_WEBSITE
:
5363 case RGW_OP_PUT_BUCKET_POLICY
:
5364 case RGW_OP_PUT_OBJ_TAGGING
:
5365 case RGW_OP_PUT_BUCKET_TAGGING
:
5366 case RGW_OP_PUT_BUCKET_REPLICATION
:
5368 case RGW_OP_SET_REQUEST_PAYMENT
:
5369 case RGW_OP_PUBSUB_NOTIF_CREATE
:
5370 case RGW_OP_PUT_BUCKET_OBJ_LOCK
:
5371 case RGW_OP_PUT_OBJ_RETENTION
:
5372 case RGW_OP_PUT_OBJ_LEGAL_HOLD
:
5373 case RGW_STS_GET_SESSION_TOKEN
:
5374 case RGW_STS_ASSUME_ROLE
:
5375 case RGW_OP_PUT_BUCKET_PUBLIC_ACCESS_BLOCK
:
5376 case RGW_OP_GET_BUCKET_PUBLIC_ACCESS_BLOCK
:
5377 case RGW_OP_DELETE_BUCKET_PUBLIC_ACCESS_BLOCK
:
5380 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
5381 throw -ERR_NOT_IMPLEMENTED
;
5384 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
5386 std::placeholders::_1
);
5391 std::move(string_to_sign
),
5396 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
5397 * it "chunked" but let's be coherent with Amazon's terminology. */
5399 dout(10) << "body content detected in multiple chunks" << dendl
;
5401 /* payload in multiple chunks */
5405 case RGW_OP_PUT_OBJ
:
5408 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
5409 throw -ERR_NOT_IMPLEMENTED
;
5412 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
5414 /* In the case of streamed payload client sets the x-amz-content-sha256
5415 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
5416 * when constructing the Canonical Request. */
5418 /* In the case of single-chunk upload client set the header's value is
5419 * coherent with the one used for Canonical Request crafting. */
5421 /* In the case of query string-based authentication there should be no
5422 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
5424 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
5429 std::placeholders::_1
);
5434 std::move(string_to_sign
),
5443 boost::optional
<std::string
>
5444 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
5445 const req_info
& info
,
5446 const boost::string_view
& signedheaders
,
5447 const bool using_qs
) const
5449 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
5454 AWSEngine::VersionAbstractor::auth_data_t
5455 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
5457 boost::string_view access_key_id
;
5458 boost::string_view signature
;
5459 boost::string_view session_token
;
5462 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
5463 if (! http_auth
|| http_auth
[0] == '\0') {
5464 /* Credentials are provided in query string. We also need to verify
5465 * the "Expires" parameter now. */
5466 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
5467 signature
= s
->info
.args
.get("Signature");
5470 boost::string_view expires
= s
->info
.args
.get("Expires");
5471 if (expires
.empty()) {
5475 /* It looks we have the guarantee that expires is a null-terminated,
5476 * and thus string_view::data() can be safely used. */
5477 const time_t exp
= atoll(expires
.data());
5484 if (s
->info
.args
.exists("X-Amz-Security-Token")) {
5485 session_token
= s
->info
.args
.get("X-Amz-Security-Token");
5486 if (session_token
.size() == 0) {
5492 /* The "Authorization" HTTP header is being used. */
5493 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
5494 const size_t pos
= auth_str
.rfind(':');
5495 if (pos
!= boost::string_view::npos
) {
5496 access_key_id
= auth_str
.substr(0, pos
);
5497 signature
= auth_str
.substr(pos
+ 1);
5500 if (s
->info
.env
->exists("HTTP_X_AMZ_SECURITY_TOKEN")) {
5501 session_token
= s
->info
.env
->get("HTTP_X_AMZ_SECURITY_TOKEN");
5502 if (session_token
.size() == 0) {
5508 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
5509 std::string string_to_sign
;
5510 utime_t header_time
;
5511 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
5513 ldpp_dout(s
, 10) << "failed to create the canonized auth header\n"
5514 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
5518 ldpp_dout(s
, 10) << "string_to_sign:\n"
5519 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
5521 if (!qsr
&& !is_time_skew_ok(header_time
)) {
5522 throw -ERR_REQUEST_TIME_SKEWED
;
5526 std::move(access_key_id
),
5527 std::move(signature
),
5528 std::move(session_token
),
5529 std::move(string_to_sign
),
5530 rgw::auth::s3::get_v2_signature
,
5531 null_completer_factory
5536 AWSEngine::VersionAbstractor::auth_data_t
5537 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
5540 s
->auth
.s3_postobj_creds
.access_key
,
5541 s
->auth
.s3_postobj_creds
.signature
,
5542 s
->auth
.s3_postobj_creds
.x_amz_security_token
,
5543 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
5544 rgw::auth::s3::get_v2_signature
,
5545 null_completer_factory
5549 AWSEngine::VersionAbstractor::auth_data_t
5550 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
5552 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
5554 /* grab access key id */
5555 const size_t pos
= credential
.find("/");
5556 const boost::string_view access_key_id
= credential
.substr(0, pos
);
5557 dout(10) << "access key id = " << access_key_id
<< dendl
;
5559 /* grab credential scope */
5560 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
5561 dout(10) << "credential scope = " << credential_scope
<< dendl
;
5563 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
5565 std::placeholders::_1
,
5566 std::placeholders::_2
,
5567 std::placeholders::_3
);
5571 s
->auth
.s3_postobj_creds
.signature
,
5572 s
->auth
.s3_postobj_creds
.x_amz_security_token
,
5573 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
5575 null_completer_factory
5579 AWSEngine::VersionAbstractor::auth_data_t
5580 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
5582 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
5583 ldpp_dout(s
, 0) << "Signature verification algorithm AWS v4"
5584 << " (AWS4-HMAC-SHA256)" << dendl
;
5585 return get_auth_data_v4(s
);
5587 ldpp_dout(s
, 0) << "Signature verification algorithm AWS v2" << dendl
;
5588 return get_auth_data_v2(s
);
5593 AWSEngine::authenticate(const DoutPrefixProvider
* dpp
, const req_state
* const s
) const
5595 /* Small reminder: an ver_abstractor is allowed to throw! */
5596 const auto auth_data
= ver_abstractor
.get_auth_data(s
);
5598 if (auth_data
.access_key_id
.empty() || auth_data
.client_signature
.empty()) {
5599 return result_t::deny(-EINVAL
);
5601 return authenticate(dpp
,
5602 auth_data
.access_key_id
,
5603 auth_data
.client_signature
,
5604 auth_data
.session_token
,
5605 auth_data
.string_to_sign
,
5606 auth_data
.signature_factory
,
5607 auth_data
.completer_factory
,
5612 } // namespace rgw::auth::s3
5614 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
5615 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
5617 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
5619 if (! cct
->_conf
->rgw_s3_auth_use_ldap
||
5620 cct
->_conf
->rgw_ldap_uri
.empty()) {
5625 std::lock_guard
<std::mutex
> lck(mtx
);
5627 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
5628 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
5629 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
5630 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
5631 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
5632 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
5634 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
5635 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
5643 bool rgw::auth::s3::LDAPEngine::valid() {
5644 std::lock_guard
<std::mutex
> lck(mtx
);
5648 rgw::auth::RemoteApplier::acl_strategy_t
5649 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
5651 //This is based on the assumption that the default acl strategy in
5652 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
5656 rgw::auth::RemoteApplier::AuthInfo
5657 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
5659 /* The short form of "using" can't be used here -- we're aliasing a class'
5661 using acct_privilege_t
= \
5662 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
5664 return rgw::auth::RemoteApplier::AuthInfo
{
5667 RGW_PERM_FULL_CONTROL
,
5668 acct_privilege_t::IS_PLAIN_ACCT
,
5673 rgw::auth::Engine::result_t
5674 rgw::auth::s3::LDAPEngine::authenticate(
5675 const DoutPrefixProvider
* dpp
,
5676 const boost::string_view
& access_key_id
,
5677 const boost::string_view
& signature
,
5678 const boost::string_view
& session_token
,
5679 const string_to_sign_t
& string_to_sign
,
5680 const signature_factory_t
&,
5681 const completer_factory_t
& completer_factory
,
5682 const req_state
* const s
) const
5684 /* boost filters and/or string_ref may throw on invalid input */
5685 rgw::RGWToken base64_token
;
5687 base64_token
= rgw::from_base64(access_key_id
);
5689 base64_token
= std::string("");
5692 if (! base64_token
.valid()) {
5693 return result_t::deny();
5696 //TODO: Uncomment, when we have a migration plan in place.
5697 //Check if a user of type other than 'ldap' is already present, if yes, then
5699 /*RGWUserInfo user_info;
5700 user_info.user_id = base64_token.id;
5701 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
5702 if (user_info.type != TYPE_LDAP) {
5703 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
5708 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
5709 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
5712 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
5713 get_creds_info(base64_token
));
5714 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
5715 } /* rgw::auth::s3::LDAPEngine::authenticate */
5717 void rgw::auth::s3::LDAPEngine::shutdown() {
5725 rgw::auth::Engine::result_t
5726 rgw::auth::s3::LocalEngine::authenticate(
5727 const DoutPrefixProvider
* dpp
,
5728 const boost::string_view
& _access_key_id
,
5729 const boost::string_view
& signature
,
5730 const boost::string_view
& session_token
,
5731 const string_to_sign_t
& string_to_sign
,
5732 const signature_factory_t
& signature_factory
,
5733 const completer_factory_t
& completer_factory
,
5734 const req_state
* const s
) const
5736 /* get the user info */
5737 RGWUserInfo user_info
;
5738 /* TODO(rzarzynski): we need to have string-view taking variant. */
5739 const std::string access_key_id
= _access_key_id
.to_string();
5740 if (rgw_get_user_info_by_access_key(ctl
->user
, access_key_id
, user_info
) < 0) {
5741 ldpp_dout(dpp
, 5) << "error reading user info, uid=" << access_key_id
5742 << " can't authenticate" << dendl
;
5743 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
5745 //TODO: Uncomment, when we have a migration plan in place.
5747 if (s->user->type != TYPE_RGW) {
5748 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << s->user->type
5749 << " is present" << dendl;
5754 const auto iter
= user_info
.access_keys
.find(access_key_id
);
5755 if (iter
== std::end(user_info
.access_keys
)) {
5756 ldpp_dout(dpp
, 0) << "ERROR: access key not encoded in user info" << dendl
;
5757 return result_t::deny(-EPERM
);
5759 const RGWAccessKey
& k
= iter
->second
;
5761 const VersionAbstractor::server_signature_t server_signature
= \
5762 signature_factory(cct
, k
.key
, string_to_sign
);
5763 auto compare
= signature
.compare(server_signature
);
5765 ldpp_dout(dpp
, 15) << "string_to_sign="
5766 << rgw::crypt_sanitize::log_content
{string_to_sign
}
5768 ldpp_dout(dpp
, 15) << "server signature=" << server_signature
<< dendl
;
5769 ldpp_dout(dpp
, 15) << "client signature=" << signature
<< dendl
;
5770 ldpp_dout(dpp
, 15) << "compare=" << compare
<< dendl
;
5773 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
5776 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
, boost::none
);
5777 return result_t::grant(std::move(apl
), completer_factory(k
.key
));
5780 rgw::auth::RemoteApplier::AuthInfo
5781 rgw::auth::s3::STSEngine::get_creds_info(const STS::SessionToken
& token
) const noexcept
5783 using acct_privilege_t
= \
5784 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
5786 return rgw::auth::RemoteApplier::AuthInfo
{
5790 (token
.is_admin
) ? acct_privilege_t::IS_ADMIN_ACCT
: acct_privilege_t::IS_PLAIN_ACCT
,
5796 rgw::auth::s3::STSEngine::get_session_token(const DoutPrefixProvider
* dpp
, const boost::string_view
& session_token
,
5797 STS::SessionToken
& token
) const
5799 string decodedSessionToken
;
5801 decodedSessionToken
= rgw::from_base64(session_token
);
5803 ldpp_dout(dpp
, 0) << "ERROR: Invalid session token, not base64 encoded." << dendl
;
5807 auto* cryptohandler
= cct
->get_crypto_handler(CEPH_CRYPTO_AES
);
5808 if (! cryptohandler
) {
5811 string secret_s
= cct
->_conf
->rgw_sts_key
;
5812 buffer::ptr
secret(secret_s
.c_str(), secret_s
.length());
5814 if (ret
= cryptohandler
->validate_secret(secret
); ret
< 0) {
5815 ldpp_dout(dpp
, 0) << "ERROR: Invalid secret key" << dendl
;
5819 auto* keyhandler
= cryptohandler
->get_key_handler(secret
, error
);
5825 string decrypted_str
;
5826 buffer::list en_input
, dec_output
;
5827 en_input
= buffer::list::static_from_string(decodedSessionToken
);
5829 ret
= keyhandler
->decrypt(en_input
, dec_output
, &error
);
5831 ldpp_dout(dpp
, 0) << "ERROR: Decryption failed: " << error
<< dendl
;
5835 dec_output
.append('\0');
5836 auto iter
= dec_output
.cbegin();
5837 decode(token
, iter
);
5838 } catch (const buffer::error
& e
) {
5839 ldout(cct
, 0) << "ERROR: decode SessionToken failed: " << error
<< dendl
;
5846 rgw::auth::Engine::result_t
5847 rgw::auth::s3::STSEngine::authenticate(
5848 const DoutPrefixProvider
* dpp
,
5849 const boost::string_view
& _access_key_id
,
5850 const boost::string_view
& signature
,
5851 const boost::string_view
& session_token
,
5852 const string_to_sign_t
& string_to_sign
,
5853 const signature_factory_t
& signature_factory
,
5854 const completer_factory_t
& completer_factory
,
5855 const req_state
* const s
) const
5857 if (! s
->info
.args
.exists("X-Amz-Security-Token") &&
5858 ! s
->info
.env
->exists("HTTP_X_AMZ_SECURITY_TOKEN") &&
5859 s
->auth
.s3_postobj_creds
.x_amz_security_token
.empty()) {
5860 return result_t::deny();
5863 STS::SessionToken token
;
5864 if (int ret
= get_session_token(dpp
, session_token
, token
); ret
< 0) {
5865 return result_t::reject(ret
);
5868 //Check if access key is not the same passed in by client
5869 if (token
.access_key_id
!= _access_key_id
) {
5870 ldpp_dout(dpp
, 0) << "Invalid access key" << dendl
;
5871 return result_t::reject(-EPERM
);
5873 //Check if the token has expired
5874 if (! token
.expiration
.empty()) {
5875 std::string expiration
= token
.expiration
;
5876 if (! expiration
.empty()) {
5877 boost::optional
<real_clock::time_point
> exp
= ceph::from_iso_8601(expiration
, false);
5879 real_clock::time_point now
= real_clock::now();
5881 ldpp_dout(dpp
, 0) << "ERROR: Token expired" << dendl
;
5882 return result_t::reject(-EPERM
);
5885 ldpp_dout(dpp
, 0) << "ERROR: Invalid expiration: " << expiration
<< dendl
;
5886 return result_t::reject(-EPERM
);
5890 //Check for signature mismatch
5891 const VersionAbstractor::server_signature_t server_signature
= \
5892 signature_factory(cct
, token
.secret_access_key
, string_to_sign
);
5893 auto compare
= signature
.compare(server_signature
);
5895 ldpp_dout(dpp
, 15) << "string_to_sign="
5896 << rgw::crypt_sanitize::log_content
{string_to_sign
}
5898 ldpp_dout(dpp
, 15) << "server signature=" << server_signature
<< dendl
;
5899 ldpp_dout(dpp
, 15) << "client signature=" << signature
<< dendl
;
5900 ldpp_dout(dpp
, 15) << "compare=" << compare
<< dendl
;
5903 return result_t::reject(-ERR_SIGNATURE_NO_MATCH
);
5906 // Get all the authorization info
5907 RGWUserInfo user_info
;
5910 rgw::auth::RoleApplier::Role r
;
5911 if (! token
.roleId
.empty()) {
5912 RGWRole
role(s
->cct
, ctl
, token
.roleId
);
5913 if (role
.get_by_id() < 0) {
5914 return result_t::deny(-EPERM
);
5916 r
.id
= token
.roleId
;
5917 r
.name
= role
.get_name();
5918 r
.tenant
= role
.get_tenant();
5920 vector
<string
> role_policy_names
= role
.get_role_policy_names();
5921 for (auto& policy_name
: role_policy_names
) {
5923 if (int ret
= role
.get_role_policy(policy_name
, perm_policy
); ret
== 0) {
5924 r
.role_policies
.push_back(std::move(perm_policy
));
5927 // This is mostly needed to assign the owner of a bucket during its creation
5928 user_id
= token
.user
;
5931 if (! token
.user
.empty() && token
.acct_type
!= TYPE_ROLE
) {
5933 int ret
= rgw_get_user_info_by_uid(ctl
->user
, token
.user
, user_info
, NULL
);
5935 ldpp_dout(dpp
, 5) << "ERROR: failed reading user info: uid=" << token
.user
<< dendl
;
5936 return result_t::reject(-EPERM
);
5940 if (token
.acct_type
== TYPE_KEYSTONE
|| token
.acct_type
== TYPE_LDAP
) {
5941 auto apl
= remote_apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
5942 get_creds_info(token
));
5943 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
5944 } else if (token
.acct_type
== TYPE_ROLE
) {
5945 auto apl
= role_apl_factory
->create_apl_role(cct
, s
, r
, user_id
, token
.policy
, token
.role_session
, token
.token_claims
);
5946 return result_t::grant(std::move(apl
), completer_factory(token
.secret_access_key
));
5947 } else { // This is for all local users of type TYPE_RGW or TYPE_NONE
5949 auto apl
= local_apl_factory
->create_apl_local(cct
, s
, user_info
, subuser
, token
.perm_mask
);
5950 return result_t::grant(std::move(apl
), completer_factory(token
.secret_access_key
));
5954 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
5957 if (s
->op
== OP_OPTIONS
) {
5963 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
5965 return route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKNOWN
;