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 <boost/algorithm/string/predicate.hpp>
10 #include "common/ceph_json.h"
11 #include "rgw_common.h"
13 #include "rgw_acl_swift.h"
16 #define dout_subsys ceph_subsys_rgw
19 #define SWIFT_PERM_READ RGW_PERM_READ_OBJS
20 #define SWIFT_PERM_WRITE RGW_PERM_WRITE_OBJS
21 /* FIXME: do we really need separate RW? */
22 #define SWIFT_PERM_RWRT (SWIFT_PERM_READ | SWIFT_PERM_WRITE)
23 #define SWIFT_PERM_ADMIN RGW_PERM_FULL_CONTROL
25 #define SWIFT_GROUP_ALL_USERS ".r:*"
29 static int parse_list(const char* uid_list
,
30 std::vector
<std::string
>& uids
) /* out */
32 char *s
= strdup(uid_list
);
38 const char *p
= strtok_r(s
, " ,", &tokctx
);
44 p
= strtok_r(NULL
, " ,", &tokctx
);
50 static bool is_referrer(const std::string
& designator
)
52 return designator
.compare(".r") == 0 ||
53 designator
.compare(".ref") == 0 ||
54 designator
.compare(".referer") == 0 ||
55 designator
.compare(".referrer") == 0;
58 static bool uid_is_public(const string
& uid
)
60 if (uid
[0] != '.' || uid
[1] != 'r')
63 int pos
= uid
.find(':');
64 if (pos
< 0 || pos
== (int)uid
.size())
67 string sub
= uid
.substr(0, pos
);
68 string after
= uid
.substr(pos
+ 1);
70 if (after
.compare("*") != 0)
73 return is_referrer(sub
);
76 static boost::optional
<ACLGrant
> referrer_to_grant(std::string url_spec
,
79 /* This function takes url_spec as non-ref std::string because of the trim
80 * operation that is essential to preserve compliance with Swift. It can't
81 * be easily accomplished with std::string_view. */
86 if ('-' == url_spec
[0]) {
87 url_spec
= url_spec
.substr(1);
88 boost::algorithm::trim(url_spec
);
95 if (url_spec
!= RGW_REFERER_WILDCARD
) {
96 if ('*' == url_spec
[0]) {
97 url_spec
= url_spec
.substr(1);
98 boost::algorithm::trim(url_spec
);
101 if (url_spec
.empty() || url_spec
== ".") {
105 /* Please be aware we're specially handling the .r:* in _add_grant()
106 * of RGWAccessControlList as the S3 API has a similar concept, and
107 * thus we can have a small portion of compatibility. */
110 grant
.set_referer(url_spec
, is_negative
? 0 : perm
);
112 } catch (const std::out_of_range
&) {
117 static ACLGrant
user_to_grant(const DoutPrefixProvider
*dpp
,
118 CephContext
* const cct
,
119 rgw::sal::Driver
* driver
,
120 const std::string
& uid
,
123 RGWUserInfo grant_user
;
125 std::unique_ptr
<rgw::sal::User
> user
;
127 user
= driver
->get_user(rgw_user(uid
));
128 if (user
->load_user(dpp
, null_yield
) < 0) {
129 ldpp_dout(dpp
, 10) << "grant user does not exist: " << uid
<< dendl
;
130 /* skipping silently */
131 grant
.set_canon(user
->get_id(), std::string(), perm
);
133 grant
.set_canon(user
->get_id(), user
->get_display_name(), perm
);
139 int RGWAccessControlPolicy_SWIFT::add_grants(const DoutPrefixProvider
*dpp
,
140 rgw::sal::Driver
* driver
,
141 const std::vector
<std::string
>& uids
,
144 for (const auto& uid
: uids
) {
145 boost::optional
<ACLGrant
> grant
;
146 ldpp_dout(dpp
, 20) << "trying to add grant for ACL uid=" << uid
<< dendl
;
148 /* Let's check whether the item has a separator potentially indicating
149 * a special meaning (like an HTTP referral-based grant). */
150 const size_t pos
= uid
.find(':');
151 if (std::string::npos
== pos
) {
152 /* No, it don't have -- we've got just a regular user identifier. */
153 grant
= user_to_grant(dpp
, cct
, driver
, uid
, perm
);
155 /* Yes, *potentially* an HTTP referral. */
156 auto designator
= uid
.substr(0, pos
);
157 auto designatee
= uid
.substr(pos
+ 1);
159 /* Swift strips whitespaces at both beginning and end. */
160 boost::algorithm::trim(designator
);
161 boost::algorithm::trim(designatee
);
163 if (! boost::algorithm::starts_with(designator
, ".")) {
164 grant
= user_to_grant(dpp
, cct
, driver
, uid
, perm
);
165 } else if ((perm
& SWIFT_PERM_WRITE
) == 0 && is_referrer(designator
)) {
166 /* HTTP referrer-based ACLs aren't acceptable for writes. */
167 grant
= referrer_to_grant(designatee
, perm
);
172 acl
.add_grant(&*grant
);
182 int RGWAccessControlPolicy_SWIFT::create(const DoutPrefixProvider
*dpp
,
183 rgw::sal::Driver
* driver
,
185 const std::string
& name
,
186 const char* read_list
,
187 const char* write_list
,
190 acl
.create_default(id
, name
);
192 owner
.set_name(name
);
196 std::vector
<std::string
> uids
;
197 int r
= parse_list(read_list
, uids
);
199 ldpp_dout(dpp
, 0) << "ERROR: parse_list for read returned r="
204 r
= add_grants(dpp
, driver
, uids
, SWIFT_PERM_READ
);
206 ldpp_dout(dpp
, 0) << "ERROR: add_grants for read returned r="
210 rw_mask
|= SWIFT_PERM_READ
;
213 std::vector
<std::string
> uids
;
214 int r
= parse_list(write_list
, uids
);
216 ldpp_dout(dpp
, 0) << "ERROR: parse_list for write returned r="
221 r
= add_grants(dpp
, driver
, uids
, SWIFT_PERM_WRITE
);
223 ldpp_dout(dpp
, 0) << "ERROR: add_grants for write returned r="
227 rw_mask
|= SWIFT_PERM_WRITE
;
232 void RGWAccessControlPolicy_SWIFT::filter_merge(uint32_t rw_mask
,
233 RGWAccessControlPolicy_SWIFT
*old
)
235 /* rw_mask&SWIFT_PERM_READ => setting read acl,
236 * rw_mask&SWIFT_PERM_WRITE => setting write acl
237 * when bit is cleared, copy matching elements from old.
239 if (rw_mask
== (SWIFT_PERM_READ
|SWIFT_PERM_WRITE
)) {
242 rw_mask
^= (SWIFT_PERM_READ
|SWIFT_PERM_WRITE
);
243 for (auto &iter
: old
->acl
.get_grant_map()) {
244 ACLGrant
& grant
= iter
.second
;
245 uint32_t perm
= grant
.get_permission().get_permissions();
248 if (!grant
.get_id(id
)) {
249 if (grant
.get_group() != ACL_GROUP_ALL_USERS
) {
250 url_spec
= grant
.get_referer();
251 if (url_spec
.empty()) {
255 /* We need to carry also negative, HTTP referrer-based ACLs. */
256 perm
= SWIFT_PERM_READ
;
260 if (perm
& rw_mask
) {
261 acl
.add_grant(&grant
);
266 void RGWAccessControlPolicy_SWIFT::to_str(string
& read
, string
& write
)
268 multimap
<string
, ACLGrant
>& m
= acl
.get_grant_map();
269 multimap
<string
, ACLGrant
>::iterator iter
;
271 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
272 ACLGrant
& grant
= iter
->second
;
273 const uint32_t perm
= grant
.get_permission().get_permissions();
276 if (!grant
.get_id(id
)) {
277 if (grant
.get_group() == ACL_GROUP_ALL_USERS
) {
278 id
= SWIFT_GROUP_ALL_USERS
;
280 url_spec
= grant
.get_referer();
281 if (url_spec
.empty()) {
284 id
= (perm
!= 0) ? ".r:" + url_spec
: ".r:-" + url_spec
;
287 if (perm
& SWIFT_PERM_READ
) {
291 read
.append(id
.to_str());
292 } else if (perm
& SWIFT_PERM_WRITE
) {
293 if (!write
.empty()) {
296 write
.append(id
.to_str());
297 } else if (perm
== 0 && !url_spec
.empty()) {
298 /* only X-Container-Read headers support referers */
302 read
.append(id
.to_str());
307 void RGWAccessControlPolicy_SWIFTAcct::add_grants(const DoutPrefixProvider
*dpp
,
308 rgw::sal::Driver
* driver
,
309 const std::vector
<std::string
>& uids
,
312 for (const auto& uid
: uids
) {
315 if (uid_is_public(uid
)) {
316 grant
.set_group(ACL_GROUP_ALL_USERS
, perm
);
317 acl
.add_grant(&grant
);
319 std::unique_ptr
<rgw::sal::User
> user
= driver
->get_user(rgw_user(uid
));
321 if (user
->load_user(dpp
, null_yield
) < 0) {
322 ldpp_dout(dpp
, 10) << "grant user does not exist:" << uid
<< dendl
;
323 /* skipping silently */
324 grant
.set_canon(user
->get_id(), std::string(), perm
);
325 acl
.add_grant(&grant
);
327 grant
.set_canon(user
->get_id(), user
->get_display_name(), perm
);
328 acl
.add_grant(&grant
);
334 bool RGWAccessControlPolicy_SWIFTAcct::create(const DoutPrefixProvider
*dpp
,
335 rgw::sal::Driver
* driver
,
337 const std::string
& name
,
338 const std::string
& acl_str
)
340 acl
.create_default(id
, name
);
342 owner
.set_name(name
);
346 if (!parser
.parse(acl_str
.c_str(), acl_str
.length())) {
347 ldpp_dout(dpp
, 0) << "ERROR: JSONParser::parse returned error=" << dendl
;
351 JSONObjIter iter
= parser
.find_first("admin");
352 if (!iter
.end() && (*iter
)->is_array()) {
353 std::vector
<std::string
> admin
;
354 decode_json_obj(admin
, *iter
);
355 ldpp_dout(dpp
, 0) << "admins: " << admin
<< dendl
;
357 add_grants(dpp
, driver
, admin
, SWIFT_PERM_ADMIN
);
360 iter
= parser
.find_first("read-write");
361 if (!iter
.end() && (*iter
)->is_array()) {
362 std::vector
<std::string
> readwrite
;
363 decode_json_obj(readwrite
, *iter
);
364 ldpp_dout(dpp
, 0) << "read-write: " << readwrite
<< dendl
;
366 add_grants(dpp
, driver
, readwrite
, SWIFT_PERM_RWRT
);
369 iter
= parser
.find_first("read-only");
370 if (!iter
.end() && (*iter
)->is_array()) {
371 std::vector
<std::string
> readonly
;
372 decode_json_obj(readonly
, *iter
);
373 ldpp_dout(dpp
, 0) << "read-only: " << readonly
<< dendl
;
375 add_grants(dpp
, driver
, readonly
, SWIFT_PERM_READ
);
381 boost::optional
<std::string
> RGWAccessControlPolicy_SWIFTAcct::to_str() const
383 std::vector
<std::string
> admin
;
384 std::vector
<std::string
> readwrite
;
385 std::vector
<std::string
> readonly
;
387 /* Parition the grant map into three not-overlapping groups. */
388 for (const auto& item
: get_acl().get_grant_map()) {
389 const ACLGrant
& grant
= item
.second
;
390 const uint32_t perm
= grant
.get_permission().get_permissions();
393 if (!grant
.get_id(id
)) {
394 if (grant
.get_group() != ACL_GROUP_ALL_USERS
) {
397 id
= SWIFT_GROUP_ALL_USERS
;
398 } else if (owner
.get_id() == id
) {
402 if (SWIFT_PERM_ADMIN
== (perm
& SWIFT_PERM_ADMIN
)) {
403 admin
.insert(admin
.end(), id
.to_str());
404 } else if (SWIFT_PERM_RWRT
== (perm
& SWIFT_PERM_RWRT
)) {
405 readwrite
.insert(readwrite
.end(), id
.to_str());
406 } else if (SWIFT_PERM_READ
== (perm
& SWIFT_PERM_READ
)) {
407 readonly
.insert(readonly
.end(), id
.to_str());
409 // FIXME: print a warning
413 /* If there is no grant to serialize, let's exit earlier to not return
414 * an empty JSON object which brakes the functional tests of Swift. */
415 if (admin
.empty() && readwrite
.empty() && readonly
.empty()) {
419 /* Serialize the groups. */
420 JSONFormatter formatter
;
422 formatter
.open_object_section("acl");
423 if (!readonly
.empty()) {
424 encode_json("read-only", readonly
, &formatter
);
426 if (!readwrite
.empty()) {
427 encode_json("read-write", readwrite
, &formatter
);
429 if (!admin
.empty()) {
430 encode_json("admin", admin
, &formatter
);
432 formatter
.close_section();
434 std::ostringstream oss
;
435 formatter
.flush(oss
);