]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_acl_swift.cc
update sources to 12.2.7
[ceph.git] / ceph / src / rgw / rgw_acl_swift.cc
CommitLineData
7c673cae
FG
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
17using 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
28e407b8 27static int parse_list(const char* uid_list,
7c673cae
FG
28 std::vector<std::string>& uids) /* out */
29{
28e407b8 30 char *s = strdup(uid_list);
7c673cae
FG
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
48static 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
56static 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
74static 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
31f18b77 93 if (url_spec != RGW_REFERER_WILDCARD) {
7c673cae
FG
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 }
31f18b77
FG
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. */
7c673cae
FG
106 }
107
31f18b77 108 grant.set_referer(url_spec, is_negative ? 0 : perm);
7c673cae
FG
109 return grant;
110 } catch (std::out_of_range) {
111 return boost::none;
112 }
113}
114
115static 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
135int 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
177int RGWAccessControlPolicy_SWIFT::create(RGWRados* const store,
178 const rgw_user& id,
179 const std::string& name,
28e407b8
AA
180 const char* read_list,
181 const char* write_list,
7c673cae
FG
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
28e407b8 189 if (read_list) {
7c673cae
FG
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 }
28e407b8 206 if (write_list) {
7c673cae
FG
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
226void 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
260void 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
301void 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
328bool 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
224ce89b 374boost::optional<std::string> RGWAccessControlPolicy_SWIFTAcct::to_str() const
7c673cae
FG
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
224ce89b
WB
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
7c673cae
FG
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
224ce89b 430 return oss.str();
7c673cae 431}