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 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 std::string& read_list,
181 const std::string& write_list,
184 acl.create_default(id, name);
186 owner.set_name(name);
189 if (read_list.size()) {
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;
206 if (write_list.size()) {
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);