Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / rgw / rgw_acl_swift.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <string.h>
5
6 #include <vector>
7
8 #include <boost/algorithm/string/predicate.hpp>
9
10 #include "common/ceph_json.h"
11 #include "rgw_common.h"
12 #include "rgw_user.h"
13 #include "rgw_acl_swift.h"
14
15 #define dout_subsys ceph_subsys_rgw
16
17 using namespace std;
18
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
24
25 #define SWIFT_GROUP_ALL_USERS ".r:*"
26
27 static int parse_list(const std::string& uid_list,
28                       std::vector<std::string>& uids)           /* out */
29 {
30   char *s = strdup(uid_list.c_str());
31   if (!s) {
32     return -ENOMEM;
33   }
34
35   char *tokctx;
36   const char *p = strtok_r(s, " ,", &tokctx);
37   while (p) {
38     if (*p) {
39       string acl = p;
40       uids.push_back(acl);
41     }
42     p = strtok_r(NULL, " ,", &tokctx);
43   }
44   free(s);
45   return 0;
46 }
47
48 static bool is_referrer(const std::string& designator)
49 {
50   return designator.compare(".r") == 0 ||
51          designator.compare(".ref") == 0 ||
52          designator.compare(".referer") == 0 ||
53          designator.compare(".referrer") == 0;
54 }
55
56 static bool uid_is_public(const string& uid)
57 {
58   if (uid[0] != '.' || uid[1] != 'r')
59     return false;
60
61   int pos = uid.find(':');
62   if (pos < 0 || pos == (int)uid.size())
63     return false;
64
65   string sub = uid.substr(0, pos);
66   string after = uid.substr(pos + 1);
67
68   if (after.compare("*") != 0)
69     return false;
70
71   return is_referrer(sub);
72 }
73
74 static boost::optional<ACLGrant> referrer_to_grant(std::string url_spec,
75                                                    const uint32_t perm)
76 {
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. */
80   try {
81     bool is_negative;
82     ACLGrant grant;
83
84     if ('-' == url_spec[0]) {
85       url_spec = url_spec.substr(1);
86       boost::algorithm::trim(url_spec);
87
88       is_negative = true;
89     } else {
90       is_negative = false;
91     }
92
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);
97       }
98
99       if (url_spec.empty() || url_spec == ".") {
100         return boost::none;
101       }
102     } else {
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. */
106     }
107
108     grant.set_referer(url_spec, is_negative ? 0 : perm);
109     return grant;
110   } catch (std::out_of_range) {
111     return boost::none;
112   }
113 }
114
115 static ACLGrant user_to_grant(CephContext* const cct,
116                               RGWRados* const store,
117                               const std::string& uid,
118                               const uint32_t perm)
119 {
120   rgw_user user(uid);
121   RGWUserInfo grant_user;
122   ACLGrant grant;
123
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);
128   } else {
129     grant.set_canon(user, grant_user.display_name, perm);
130   }
131
132   return grant;
133 }
134
135 int RGWAccessControlPolicy_SWIFT::add_grants(RGWRados* const store,
136                                              const std::vector<std::string>& uids,
137                                              const uint32_t perm)
138 {
139   for (const auto& uid : uids) {
140     boost::optional<ACLGrant> grant;
141     ldout(cct, 20) << "trying to add grant for ACL uid=" << uid << dendl;
142
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);
149     } else {
150       /* Yes, *potentially* an HTTP referral. */
151       auto designator = uid.substr(0, pos);
152       auto designatee = uid.substr(pos + 1);
153
154       /* Swift strips whitespaces at both beginning and end. */
155       boost::algorithm::trim(designator);
156       boost::algorithm::trim(designatee);
157
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);
163       }
164     }
165
166     if (grant) {
167       acl.add_grant(&*grant);
168     } else {
169       return -EINVAL;
170     }
171   }
172
173   return 0;
174 }
175
176
177 int RGWAccessControlPolicy_SWIFT::create(RGWRados* const store,
178                                          const rgw_user& id,
179                                          const std::string& name,
180                                          const std::string& read_list,
181                                          const std::string& write_list,
182                                          uint32_t& rw_mask)
183 {
184   acl.create_default(id, name);
185   owner.set_id(id);
186   owner.set_name(name);
187   rw_mask = 0;
188
189   if (read_list.size()) {
190     std::vector<std::string> uids;
191     int r = parse_list(read_list, uids);
192     if (r < 0) {
193       ldout(cct, 0) << "ERROR: parse_list for read returned r="
194                     << r << dendl;
195       return r;
196     }
197
198     r = add_grants(store, uids, SWIFT_PERM_READ);
199     if (r < 0) {
200       ldout(cct, 0) << "ERROR: add_grants for read returned r="
201                     << r << dendl;
202       return r;
203     }
204     rw_mask |= SWIFT_PERM_READ;
205   }
206   if (write_list.size()) {
207     std::vector<std::string> uids;
208     int r = parse_list(write_list, uids);
209     if (r < 0) {
210       ldout(cct, 0) << "ERROR: parse_list for write returned r="
211                     << r << dendl;
212       return r;
213     }
214
215     r = add_grants(store, uids, SWIFT_PERM_WRITE);
216     if (r < 0) {
217       ldout(cct, 0) << "ERROR: add_grants for write returned r="
218                     << r << dendl;
219       return r;
220     }
221     rw_mask |= SWIFT_PERM_WRITE;
222   }
223   return 0;
224 }
225
226 void RGWAccessControlPolicy_SWIFT::filter_merge(uint32_t rw_mask,
227                                                 RGWAccessControlPolicy_SWIFT *old)
228 {
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.
232    */
233   if (rw_mask == (SWIFT_PERM_READ|SWIFT_PERM_WRITE)) {
234     return;
235   }
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();
240     rgw_user id;
241     string url_spec;
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()) {
246           continue;
247         }
248         if (perm == 0) {
249           /* We need to carry also negative, HTTP referrer-based ACLs. */
250           perm = SWIFT_PERM_READ;
251         }
252       }
253     }
254     if (perm & rw_mask) {
255       acl.add_grant(&grant);
256     }
257   }
258 }
259
260 void RGWAccessControlPolicy_SWIFT::to_str(string& read, string& write)
261 {
262   multimap<string, ACLGrant>& m = acl.get_grant_map();
263   multimap<string, ACLGrant>::iterator iter;
264
265   for (iter = m.begin(); iter != m.end(); ++iter) {
266     ACLGrant& grant = iter->second;
267     const uint32_t perm = grant.get_permission().get_permissions();
268     rgw_user id;
269     string url_spec;
270     if (!grant.get_id(id)) {
271       if (grant.get_group() == ACL_GROUP_ALL_USERS) {
272         id = SWIFT_GROUP_ALL_USERS;
273       } else {
274         url_spec = grant.get_referer();
275         if (url_spec.empty()) {
276           continue;
277         }
278         id = (perm != 0) ? ".r:" + url_spec : ".r:-" + url_spec;
279       }
280     }
281     if (perm & SWIFT_PERM_READ) {
282       if (!read.empty()) {
283         read.append(",");
284       }
285       read.append(id.to_str());
286     } else if (perm & SWIFT_PERM_WRITE) {
287       if (!write.empty()) {
288         write.append(",");
289       }
290       write.append(id.to_str());
291     } else if (perm == 0 && !url_spec.empty()) {
292       /* only X-Container-Read headers support referers */
293       if (!read.empty()) {
294         read.append(",");
295       }
296       read.append(id.to_str());
297     }
298   }
299 }
300
301 void RGWAccessControlPolicy_SWIFTAcct::add_grants(RGWRados * const store,
302                                                   const std::vector<std::string>& uids,
303                                                   const uint32_t perm)
304 {
305   for (const auto& uid : uids) {
306     ACLGrant grant;
307     RGWUserInfo grant_user;
308
309     if (uid_is_public(uid)) {
310       grant.set_group(ACL_GROUP_ALL_USERS, perm);
311       acl.add_grant(&grant);
312     } else  {
313       rgw_user user(uid);
314
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);
320       } else {
321         grant.set_canon(user, grant_user.display_name, perm);
322         acl.add_grant(&grant);
323       }
324     }
325   }
326 }
327
328 bool RGWAccessControlPolicy_SWIFTAcct::create(RGWRados * const store,
329                                               const rgw_user& id,
330                                               const std::string& name,
331                                               const std::string& acl_str)
332 {
333   acl.create_default(id, name);
334   owner.set_id(id);
335   owner.set_name(name);
336
337   JSONParser parser;
338
339   if (!parser.parse(acl_str.c_str(), acl_str.length())) {
340     ldout(cct, 0) << "ERROR: JSONParser::parse returned error=" << dendl;
341     return false;
342   }
343
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;
349
350     add_grants(store, admin, SWIFT_PERM_ADMIN);
351   }
352
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;
358
359     add_grants(store, readwrite, SWIFT_PERM_RWRT);
360   }
361
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;
367
368     add_grants(store, readonly, SWIFT_PERM_READ);
369   }
370
371   return true;
372 }
373
374 boost::optional<std::string> RGWAccessControlPolicy_SWIFTAcct::to_str() const
375 {
376   std::vector<std::string> admin;
377   std::vector<std::string> readwrite;
378   std::vector<std::string> readonly;
379
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();
384
385     rgw_user id;
386     if (!grant.get_id(id)) {
387       if (grant.get_group() != ACL_GROUP_ALL_USERS) {
388         continue;
389       }
390       id = SWIFT_GROUP_ALL_USERS;
391     } else if (owner.get_id() == id) {
392       continue;
393     }
394
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());
401     } else {
402       // FIXME: print a warning
403     }
404   }
405
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()) {
409     return boost::none;
410   }
411
412   /* Serialize the groups. */
413   JSONFormatter formatter;
414
415   formatter.open_object_section("acl");
416   if (!readonly.empty()) {
417     encode_json("read-only", readonly, &formatter);
418   }
419   if (!readwrite.empty()) {
420     encode_json("read-write", readwrite, &formatter);
421   }
422   if (!admin.empty()) {
423     encode_json("admin", admin, &formatter);
424   }
425   formatter.close_section();
426
427   std::ostringstream oss;
428   formatter.flush(oss);
429
430   return oss.str();
431 }