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 std::string
& uid_list
,
28 std::vector
<std::string
>& uids
) /* out */
30 char *s
= strdup(uid_list
.c_str());
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 /* We're specially handling the .r:* as the S3 API has a similar concept
94 * and thus we can have a small portion of compatibility here. */
95 if (url_spec
== "*") {
96 grant
.set_group(ACL_GROUP_ALL_USERS
, is_negative
? 0 : perm
);
98 if ('*' == url_spec
[0]) {
99 url_spec
= url_spec
.substr(1);
100 boost::algorithm::trim(url_spec
);
103 if (url_spec
.empty() || url_spec
== ".") {
107 grant
.set_referer(url_spec
, is_negative
? 0 : perm
);
111 } catch (std::out_of_range
) {
116 static ACLGrant
user_to_grant(CephContext
* const cct
,
117 RGWRados
* const store
,
118 const std::string
& uid
,
122 RGWUserInfo grant_user
;
125 if (rgw_get_user_info_by_uid(store
, user
, grant_user
) < 0) {
126 ldout(cct
, 10) << "grant user does not exist: " << uid
<< dendl
;
127 /* skipping silently */
128 grant
.set_canon(user
, std::string(), perm
);
130 grant
.set_canon(user
, grant_user
.display_name
, perm
);
136 int RGWAccessControlPolicy_SWIFT::add_grants(RGWRados
* const store
,
137 const std::vector
<std::string
>& uids
,
140 for (const auto& uid
: uids
) {
141 boost::optional
<ACLGrant
> grant
;
142 ldout(cct
, 20) << "trying to add grant for ACL uid=" << uid
<< dendl
;
144 /* Let's check whether the item has a separator potentially indicating
145 * a special meaning (like an HTTP referral-based grant). */
146 const size_t pos
= uid
.find(':');
147 if (std::string::npos
== pos
) {
148 /* No, it don't have -- we've got just a regular user identifier. */
149 grant
= user_to_grant(cct
, store
, uid
, perm
);
151 /* Yes, *potentially* an HTTP referral. */
152 auto designator
= uid
.substr(0, pos
);
153 auto designatee
= uid
.substr(pos
+ 1);
155 /* Swift strips whitespaces at both beginning and end. */
156 boost::algorithm::trim(designator
);
157 boost::algorithm::trim(designatee
);
159 if (! boost::algorithm::starts_with(designator
, ".")) {
160 grant
= user_to_grant(cct
, store
, uid
, perm
);
161 } else if ((perm
& SWIFT_PERM_WRITE
) == 0 && is_referrer(designator
)) {
162 /* HTTP referrer-based ACLs aren't acceptable for writes. */
163 grant
= referrer_to_grant(designatee
, perm
);
168 acl
.add_grant(&*grant
);
178 int RGWAccessControlPolicy_SWIFT::create(RGWRados
* const store
,
180 const std::string
& name
,
181 const std::string
& read_list
,
182 const std::string
& write_list
,
185 acl
.create_default(id
, name
);
187 owner
.set_name(name
);
190 if (read_list
.size()) {
191 std::vector
<std::string
> uids
;
192 int r
= parse_list(read_list
, uids
);
194 ldout(cct
, 0) << "ERROR: parse_list for read returned r="
199 r
= add_grants(store
, uids
, SWIFT_PERM_READ
);
201 ldout(cct
, 0) << "ERROR: add_grants for read returned r="
205 rw_mask
|= SWIFT_PERM_READ
;
207 if (write_list
.size()) {
208 std::vector
<std::string
> uids
;
209 int r
= parse_list(write_list
, uids
);
211 ldout(cct
, 0) << "ERROR: parse_list for write returned r="
216 r
= add_grants(store
, uids
, SWIFT_PERM_WRITE
);
218 ldout(cct
, 0) << "ERROR: add_grants for write returned r="
222 rw_mask
|= SWIFT_PERM_WRITE
;
227 void RGWAccessControlPolicy_SWIFT::filter_merge(uint32_t rw_mask
,
228 RGWAccessControlPolicy_SWIFT
*old
)
230 /* rw_mask&SWIFT_PERM_READ => setting read acl,
231 * rw_mask&SWIFT_PERM_WRITE => setting write acl
232 * when bit is cleared, copy matching elements from old.
234 if (rw_mask
== (SWIFT_PERM_READ
|SWIFT_PERM_WRITE
)) {
237 rw_mask
^= (SWIFT_PERM_READ
|SWIFT_PERM_WRITE
);
238 for (auto &iter
: old
->acl
.get_grant_map()) {
239 ACLGrant
& grant
= iter
.second
;
240 uint32_t perm
= grant
.get_permission().get_permissions();
243 if (!grant
.get_id(id
)) {
244 if (grant
.get_group() != ACL_GROUP_ALL_USERS
) {
245 url_spec
= grant
.get_referer();
246 if (url_spec
.empty()) {
250 /* We need to carry also negative, HTTP referrer-based ACLs. */
251 perm
= SWIFT_PERM_READ
;
255 if (perm
& rw_mask
) {
256 acl
.add_grant(&grant
);
261 void RGWAccessControlPolicy_SWIFT::to_str(string
& read
, string
& write
)
263 multimap
<string
, ACLGrant
>& m
= acl
.get_grant_map();
264 multimap
<string
, ACLGrant
>::iterator iter
;
266 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
267 ACLGrant
& grant
= iter
->second
;
268 const uint32_t perm
= grant
.get_permission().get_permissions();
271 if (!grant
.get_id(id
)) {
272 if (grant
.get_group() == ACL_GROUP_ALL_USERS
) {
273 id
= SWIFT_GROUP_ALL_USERS
;
275 url_spec
= grant
.get_referer();
276 if (url_spec
.empty()) {
279 id
= (perm
!= 0) ? ".r:" + url_spec
: ".r:-" + url_spec
;
282 if (perm
& SWIFT_PERM_READ
) {
286 read
.append(id
.to_str());
287 } else if (perm
& SWIFT_PERM_WRITE
) {
288 if (!write
.empty()) {
291 write
.append(id
.to_str());
292 } else if (perm
== 0 && !url_spec
.empty()) {
293 /* only X-Container-Read headers support referers */
297 read
.append(id
.to_str());
302 void RGWAccessControlPolicy_SWIFTAcct::add_grants(RGWRados
* const store
,
303 const std::vector
<std::string
>& uids
,
306 for (const auto& uid
: uids
) {
308 RGWUserInfo grant_user
;
310 if (uid_is_public(uid
)) {
311 grant
.set_group(ACL_GROUP_ALL_USERS
, perm
);
312 acl
.add_grant(&grant
);
316 if (rgw_get_user_info_by_uid(store
, user
, grant_user
) < 0) {
317 ldout(cct
, 10) << "grant user does not exist:" << uid
<< dendl
;
318 /* skipping silently */
319 grant
.set_canon(user
, std::string(), perm
);
320 acl
.add_grant(&grant
);
322 grant
.set_canon(user
, grant_user
.display_name
, perm
);
323 acl
.add_grant(&grant
);
329 bool RGWAccessControlPolicy_SWIFTAcct::create(RGWRados
* const store
,
331 const std::string
& name
,
332 const std::string
& acl_str
)
334 acl
.create_default(id
, name
);
336 owner
.set_name(name
);
340 if (!parser
.parse(acl_str
.c_str(), acl_str
.length())) {
341 ldout(cct
, 0) << "ERROR: JSONParser::parse returned error=" << dendl
;
345 JSONObjIter iter
= parser
.find_first("admin");
346 if (!iter
.end() && (*iter
)->is_array()) {
347 std::vector
<std::string
> admin
;
348 decode_json_obj(admin
, *iter
);
349 ldout(cct
, 0) << "admins: " << admin
<< dendl
;
351 add_grants(store
, admin
, SWIFT_PERM_ADMIN
);
354 iter
= parser
.find_first("read-write");
355 if (!iter
.end() && (*iter
)->is_array()) {
356 std::vector
<std::string
> readwrite
;
357 decode_json_obj(readwrite
, *iter
);
358 ldout(cct
, 0) << "read-write: " << readwrite
<< dendl
;
360 add_grants(store
, readwrite
, SWIFT_PERM_RWRT
);
363 iter
= parser
.find_first("read-only");
364 if (!iter
.end() && (*iter
)->is_array()) {
365 std::vector
<std::string
> readonly
;
366 decode_json_obj(readonly
, *iter
);
367 ldout(cct
, 0) << "read-only: " << readonly
<< dendl
;
369 add_grants(store
, readonly
, SWIFT_PERM_READ
);
375 void RGWAccessControlPolicy_SWIFTAcct::to_str(std::string
& acl_str
) const
377 std::vector
<std::string
> admin
;
378 std::vector
<std::string
> readwrite
;
379 std::vector
<std::string
> readonly
;
381 /* Parition the grant map into three not-overlapping groups. */
382 for (const auto& item
: get_acl().get_grant_map()) {
383 const ACLGrant
& grant
= item
.second
;
384 const uint32_t perm
= grant
.get_permission().get_permissions();
387 if (!grant
.get_id(id
)) {
388 if (grant
.get_group() != ACL_GROUP_ALL_USERS
) {
391 id
= SWIFT_GROUP_ALL_USERS
;
392 } else if (owner
.get_id() == id
) {
396 if (SWIFT_PERM_ADMIN
== (perm
& SWIFT_PERM_ADMIN
)) {
397 admin
.insert(admin
.end(), id
.to_str());
398 } else if (SWIFT_PERM_RWRT
== (perm
& SWIFT_PERM_RWRT
)) {
399 readwrite
.insert(readwrite
.end(), id
.to_str());
400 } else if (SWIFT_PERM_READ
== (perm
& SWIFT_PERM_READ
)) {
401 readonly
.insert(readonly
.end(), id
.to_str());
403 // FIXME: print a warning
407 /* Serialize the groups. */
408 JSONFormatter formatter
;
410 formatter
.open_object_section("acl");
411 if (!readonly
.empty()) {
412 encode_json("read-only", readonly
, &formatter
);
414 if (!readwrite
.empty()) {
415 encode_json("read-write", readwrite
, &formatter
);
417 if (!admin
.empty()) {
418 encode_json("admin", admin
, &formatter
);
420 formatter
.close_section();
422 std::ostringstream oss
;
423 formatter
.flush(oss
);