1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
8 #include <boost/algorithm/string/predicate.hpp>
10 #include "common/ceph_json.h"
11 #include "rgw_common.h"
13 #include "rgw_acl_swift.h"
15 #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:*"
27 static int parse_list(const char* uid_list
,
28 std::vector
<std::string
>& uids
) /* out */
30 char *s
= strdup(uid_list
);
36 const char *p
= strtok_r(s
, " ,", &tokctx
);
42 p
= strtok_r(NULL
, " ,", &tokctx
);
48 static bool is_referrer(const std::string
& designator
)
50 return designator
.compare(".r") == 0 ||
51 designator
.compare(".ref") == 0 ||
52 designator
.compare(".referer") == 0 ||
53 designator
.compare(".referrer") == 0;
56 static bool uid_is_public(const string
& uid
)
58 if (uid
[0] != '.' || uid
[1] != 'r')
61 int pos
= uid
.find(':');
62 if (pos
< 0 || pos
== (int)uid
.size())
65 string sub
= uid
.substr(0, pos
);
66 string after
= uid
.substr(pos
+ 1);
68 if (after
.compare("*") != 0)
71 return is_referrer(sub
);
74 static boost::optional
<ACLGrant
> referrer_to_grant(std::string url_spec
,
77 /* This function takes url_spec as non-ref std::string because of the trim
78 * operation that is essential to preserve compliance with Swift. It can't
79 * be easily accomplished with boost::string_ref. */
84 if ('-' == url_spec
[0]) {
85 url_spec
= url_spec
.substr(1);
86 boost::algorithm::trim(url_spec
);
93 if (url_spec
!= RGW_REFERER_WILDCARD
) {
94 if ('*' == url_spec
[0]) {
95 url_spec
= url_spec
.substr(1);
96 boost::algorithm::trim(url_spec
);
99 if (url_spec
.empty() || url_spec
== ".") {
103 /* Please be aware we're specially handling the .r:* in _add_grant()
104 * of RGWAccessControlList as the S3 API has a similar concept, and
105 * thus we can have a small portion of compatibility. */
108 grant
.set_referer(url_spec
, is_negative
? 0 : perm
);
110 } catch (std::out_of_range
) {
115 static ACLGrant
user_to_grant(CephContext
* const cct
,
116 RGWRados
* const store
,
117 const std::string
& uid
,
121 RGWUserInfo grant_user
;
124 if (rgw_get_user_info_by_uid(store
, user
, grant_user
) < 0) {
125 ldout(cct
, 10) << "grant user does not exist: " << uid
<< dendl
;
126 /* skipping silently */
127 grant
.set_canon(user
, std::string(), perm
);
129 grant
.set_canon(user
, grant_user
.display_name
, perm
);
135 int RGWAccessControlPolicy_SWIFT::add_grants(RGWRados
* const store
,
136 const std::vector
<std::string
>& uids
,
139 for (const auto& uid
: uids
) {
140 boost::optional
<ACLGrant
> grant
;
141 ldout(cct
, 20) << "trying to add grant for ACL uid=" << uid
<< dendl
;
143 /* Let's check whether the item has a separator potentially indicating
144 * a special meaning (like an HTTP referral-based grant). */
145 const size_t pos
= uid
.find(':');
146 if (std::string::npos
== pos
) {
147 /* No, it don't have -- we've got just a regular user identifier. */
148 grant
= user_to_grant(cct
, store
, uid
, perm
);
150 /* Yes, *potentially* an HTTP referral. */
151 auto designator
= uid
.substr(0, pos
);
152 auto designatee
= uid
.substr(pos
+ 1);
154 /* Swift strips whitespaces at both beginning and end. */
155 boost::algorithm::trim(designator
);
156 boost::algorithm::trim(designatee
);
158 if (! boost::algorithm::starts_with(designator
, ".")) {
159 grant
= user_to_grant(cct
, store
, uid
, perm
);
160 } else if ((perm
& SWIFT_PERM_WRITE
) == 0 && is_referrer(designator
)) {
161 /* HTTP referrer-based ACLs aren't acceptable for writes. */
162 grant
= referrer_to_grant(designatee
, perm
);
167 acl
.add_grant(&*grant
);
177 int RGWAccessControlPolicy_SWIFT::create(RGWRados
* const store
,
179 const std::string
& name
,
180 const char* read_list
,
181 const char* write_list
,
184 acl
.create_default(id
, name
);
186 owner
.set_name(name
);
190 std::vector
<std::string
> uids
;
191 int r
= parse_list(read_list
, uids
);
193 ldout(cct
, 0) << "ERROR: parse_list for read returned r="
198 r
= add_grants(store
, uids
, SWIFT_PERM_READ
);
200 ldout(cct
, 0) << "ERROR: add_grants for read returned r="
204 rw_mask
|= SWIFT_PERM_READ
;
207 std::vector
<std::string
> uids
;
208 int r
= parse_list(write_list
, uids
);
210 ldout(cct
, 0) << "ERROR: parse_list for write returned r="
215 r
= add_grants(store
, uids
, SWIFT_PERM_WRITE
);
217 ldout(cct
, 0) << "ERROR: add_grants for write returned r="
221 rw_mask
|= SWIFT_PERM_WRITE
;
226 void RGWAccessControlPolicy_SWIFT::filter_merge(uint32_t rw_mask
,
227 RGWAccessControlPolicy_SWIFT
*old
)
229 /* rw_mask&SWIFT_PERM_READ => setting read acl,
230 * rw_mask&SWIFT_PERM_WRITE => setting write acl
231 * when bit is cleared, copy matching elements from old.
233 if (rw_mask
== (SWIFT_PERM_READ
|SWIFT_PERM_WRITE
)) {
236 rw_mask
^= (SWIFT_PERM_READ
|SWIFT_PERM_WRITE
);
237 for (auto &iter
: old
->acl
.get_grant_map()) {
238 ACLGrant
& grant
= iter
.second
;
239 uint32_t perm
= grant
.get_permission().get_permissions();
242 if (!grant
.get_id(id
)) {
243 if (grant
.get_group() != ACL_GROUP_ALL_USERS
) {
244 url_spec
= grant
.get_referer();
245 if (url_spec
.empty()) {
249 /* We need to carry also negative, HTTP referrer-based ACLs. */
250 perm
= SWIFT_PERM_READ
;
254 if (perm
& rw_mask
) {
255 acl
.add_grant(&grant
);
260 void RGWAccessControlPolicy_SWIFT::to_str(string
& read
, string
& write
)
262 multimap
<string
, ACLGrant
>& m
= acl
.get_grant_map();
263 multimap
<string
, ACLGrant
>::iterator iter
;
265 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
266 ACLGrant
& grant
= iter
->second
;
267 const uint32_t perm
= grant
.get_permission().get_permissions();
270 if (!grant
.get_id(id
)) {
271 if (grant
.get_group() == ACL_GROUP_ALL_USERS
) {
272 id
= SWIFT_GROUP_ALL_USERS
;
274 url_spec
= grant
.get_referer();
275 if (url_spec
.empty()) {
278 id
= (perm
!= 0) ? ".r:" + url_spec
: ".r:-" + url_spec
;
281 if (perm
& SWIFT_PERM_READ
) {
285 read
.append(id
.to_str());
286 } else if (perm
& SWIFT_PERM_WRITE
) {
287 if (!write
.empty()) {
290 write
.append(id
.to_str());
291 } else if (perm
== 0 && !url_spec
.empty()) {
292 /* only X-Container-Read headers support referers */
296 read
.append(id
.to_str());
301 void RGWAccessControlPolicy_SWIFTAcct::add_grants(RGWRados
* const store
,
302 const std::vector
<std::string
>& uids
,
305 for (const auto& uid
: uids
) {
307 RGWUserInfo grant_user
;
309 if (uid_is_public(uid
)) {
310 grant
.set_group(ACL_GROUP_ALL_USERS
, perm
);
311 acl
.add_grant(&grant
);
315 if (rgw_get_user_info_by_uid(store
, user
, grant_user
) < 0) {
316 ldout(cct
, 10) << "grant user does not exist:" << uid
<< dendl
;
317 /* skipping silently */
318 grant
.set_canon(user
, std::string(), perm
);
319 acl
.add_grant(&grant
);
321 grant
.set_canon(user
, grant_user
.display_name
, perm
);
322 acl
.add_grant(&grant
);
328 bool RGWAccessControlPolicy_SWIFTAcct::create(RGWRados
* const store
,
330 const std::string
& name
,
331 const std::string
& acl_str
)
333 acl
.create_default(id
, name
);
335 owner
.set_name(name
);
339 if (!parser
.parse(acl_str
.c_str(), acl_str
.length())) {
340 ldout(cct
, 0) << "ERROR: JSONParser::parse returned error=" << dendl
;
344 JSONObjIter iter
= parser
.find_first("admin");
345 if (!iter
.end() && (*iter
)->is_array()) {
346 std::vector
<std::string
> admin
;
347 decode_json_obj(admin
, *iter
);
348 ldout(cct
, 0) << "admins: " << admin
<< dendl
;
350 add_grants(store
, admin
, SWIFT_PERM_ADMIN
);
353 iter
= parser
.find_first("read-write");
354 if (!iter
.end() && (*iter
)->is_array()) {
355 std::vector
<std::string
> readwrite
;
356 decode_json_obj(readwrite
, *iter
);
357 ldout(cct
, 0) << "read-write: " << readwrite
<< dendl
;
359 add_grants(store
, readwrite
, SWIFT_PERM_RWRT
);
362 iter
= parser
.find_first("read-only");
363 if (!iter
.end() && (*iter
)->is_array()) {
364 std::vector
<std::string
> readonly
;
365 decode_json_obj(readonly
, *iter
);
366 ldout(cct
, 0) << "read-only: " << readonly
<< dendl
;
368 add_grants(store
, readonly
, SWIFT_PERM_READ
);
374 boost::optional
<std::string
> RGWAccessControlPolicy_SWIFTAcct::to_str() const
376 std::vector
<std::string
> admin
;
377 std::vector
<std::string
> readwrite
;
378 std::vector
<std::string
> readonly
;
380 /* Parition the grant map into three not-overlapping groups. */
381 for (const auto& item
: get_acl().get_grant_map()) {
382 const ACLGrant
& grant
= item
.second
;
383 const uint32_t perm
= grant
.get_permission().get_permissions();
386 if (!grant
.get_id(id
)) {
387 if (grant
.get_group() != ACL_GROUP_ALL_USERS
) {
390 id
= SWIFT_GROUP_ALL_USERS
;
391 } else if (owner
.get_id() == id
) {
395 if (SWIFT_PERM_ADMIN
== (perm
& SWIFT_PERM_ADMIN
)) {
396 admin
.insert(admin
.end(), id
.to_str());
397 } else if (SWIFT_PERM_RWRT
== (perm
& SWIFT_PERM_RWRT
)) {
398 readwrite
.insert(readwrite
.end(), id
.to_str());
399 } else if (SWIFT_PERM_READ
== (perm
& SWIFT_PERM_READ
)) {
400 readonly
.insert(readonly
.end(), id
.to_str());
402 // FIXME: print a warning
406 /* If there is no grant to serialize, let's exit earlier to not return
407 * an empty JSON object which brakes the functional tests of Swift. */
408 if (admin
.empty() && readwrite
.empty() && readonly
.empty()) {
412 /* Serialize the groups. */
413 JSONFormatter formatter
;
415 formatter
.open_object_section("acl");
416 if (!readonly
.empty()) {
417 encode_json("read-only", readonly
, &formatter
);
419 if (!readwrite
.empty()) {
420 encode_json("read-write", readwrite
, &formatter
);
422 if (!admin
.empty()) {
423 encode_json("admin", admin
, &formatter
);
425 formatter
.close_section();
427 std::ostringstream oss
;
428 formatter
.flush(oss
);