]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_acl_swift.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / rgw / rgw_acl_swift.cc
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae
FG
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
7c673cae
FG
17
18#define SWIFT_PERM_READ RGW_PERM_READ_OBJS
19#define SWIFT_PERM_WRITE RGW_PERM_WRITE_OBJS
20/* FIXME: do we really need separate RW? */
21#define SWIFT_PERM_RWRT (SWIFT_PERM_READ | SWIFT_PERM_WRITE)
22#define SWIFT_PERM_ADMIN RGW_PERM_FULL_CONTROL
23
24#define SWIFT_GROUP_ALL_USERS ".r:*"
25
28e407b8 26static int parse_list(const char* uid_list,
7c673cae
FG
27 std::vector<std::string>& uids) /* out */
28{
28e407b8 29 char *s = strdup(uid_list);
7c673cae
FG
30 if (!s) {
31 return -ENOMEM;
32 }
33
34 char *tokctx;
35 const char *p = strtok_r(s, " ,", &tokctx);
36 while (p) {
37 if (*p) {
38 string acl = p;
39 uids.push_back(acl);
40 }
41 p = strtok_r(NULL, " ,", &tokctx);
42 }
43 free(s);
44 return 0;
45}
46
47static bool is_referrer(const std::string& designator)
48{
49 return designator.compare(".r") == 0 ||
50 designator.compare(".ref") == 0 ||
51 designator.compare(".referer") == 0 ||
52 designator.compare(".referrer") == 0;
53}
54
55static bool uid_is_public(const string& uid)
56{
57 if (uid[0] != '.' || uid[1] != 'r')
58 return false;
59
60 int pos = uid.find(':');
61 if (pos < 0 || pos == (int)uid.size())
62 return false;
63
64 string sub = uid.substr(0, pos);
65 string after = uid.substr(pos + 1);
66
67 if (after.compare("*") != 0)
68 return false;
69
70 return is_referrer(sub);
71}
72
73static boost::optional<ACLGrant> referrer_to_grant(std::string url_spec,
74 const uint32_t perm)
75{
76 /* This function takes url_spec as non-ref std::string because of the trim
77 * operation that is essential to preserve compliance with Swift. It can't
78 * be easily accomplished with boost::string_ref. */
79 try {
80 bool is_negative;
81 ACLGrant grant;
82
83 if ('-' == url_spec[0]) {
84 url_spec = url_spec.substr(1);
85 boost::algorithm::trim(url_spec);
86
87 is_negative = true;
88 } else {
89 is_negative = false;
90 }
91
31f18b77 92 if (url_spec != RGW_REFERER_WILDCARD) {
7c673cae
FG
93 if ('*' == url_spec[0]) {
94 url_spec = url_spec.substr(1);
95 boost::algorithm::trim(url_spec);
96 }
97
98 if (url_spec.empty() || url_spec == ".") {
99 return boost::none;
100 }
31f18b77
FG
101 } else {
102 /* Please be aware we're specially handling the .r:* in _add_grant()
103 * of RGWAccessControlList as the S3 API has a similar concept, and
104 * thus we can have a small portion of compatibility. */
7c673cae
FG
105 }
106
31f18b77 107 grant.set_referer(url_spec, is_negative ? 0 : perm);
7c673cae 108 return grant;
11fdf7f2 109 } catch (const std::out_of_range&) {
7c673cae
FG
110 return boost::none;
111 }
112}
113
114static ACLGrant user_to_grant(CephContext* const cct,
9f95a23c 115 RGWUserCtl* const user_ctl,
7c673cae
FG
116 const std::string& uid,
117 const uint32_t perm)
118{
119 rgw_user user(uid);
120 RGWUserInfo grant_user;
121 ACLGrant grant;
122
9f95a23c 123 if (user_ctl->get_info_by_uid(user, &grant_user, null_yield) < 0) {
7c673cae
FG
124 ldout(cct, 10) << "grant user does not exist: " << uid << dendl;
125 /* skipping silently */
126 grant.set_canon(user, std::string(), perm);
127 } else {
128 grant.set_canon(user, grant_user.display_name, perm);
129 }
130
131 return grant;
132}
133
9f95a23c 134int RGWAccessControlPolicy_SWIFT::add_grants(RGWUserCtl* const user_ctl,
7c673cae
FG
135 const std::vector<std::string>& uids,
136 const uint32_t perm)
137{
138 for (const auto& uid : uids) {
139 boost::optional<ACLGrant> grant;
140 ldout(cct, 20) << "trying to add grant for ACL uid=" << uid << dendl;
141
142 /* Let's check whether the item has a separator potentially indicating
143 * a special meaning (like an HTTP referral-based grant). */
144 const size_t pos = uid.find(':');
145 if (std::string::npos == pos) {
146 /* No, it don't have -- we've got just a regular user identifier. */
9f95a23c 147 grant = user_to_grant(cct, user_ctl, uid, perm);
7c673cae
FG
148 } else {
149 /* Yes, *potentially* an HTTP referral. */
150 auto designator = uid.substr(0, pos);
151 auto designatee = uid.substr(pos + 1);
152
153 /* Swift strips whitespaces at both beginning and end. */
154 boost::algorithm::trim(designator);
155 boost::algorithm::trim(designatee);
156
157 if (! boost::algorithm::starts_with(designator, ".")) {
9f95a23c 158 grant = user_to_grant(cct, user_ctl, uid, perm);
7c673cae
FG
159 } else if ((perm & SWIFT_PERM_WRITE) == 0 && is_referrer(designator)) {
160 /* HTTP referrer-based ACLs aren't acceptable for writes. */
161 grant = referrer_to_grant(designatee, perm);
162 }
163 }
164
165 if (grant) {
166 acl.add_grant(&*grant);
167 } else {
168 return -EINVAL;
169 }
170 }
171
172 return 0;
173}
174
175
9f95a23c 176int RGWAccessControlPolicy_SWIFT::create(RGWUserCtl* const user_ctl,
7c673cae
FG
177 const rgw_user& id,
178 const std::string& name,
28e407b8
AA
179 const char* read_list,
180 const char* write_list,
7c673cae
FG
181 uint32_t& rw_mask)
182{
183 acl.create_default(id, name);
184 owner.set_id(id);
185 owner.set_name(name);
186 rw_mask = 0;
187
28e407b8 188 if (read_list) {
7c673cae
FG
189 std::vector<std::string> uids;
190 int r = parse_list(read_list, uids);
191 if (r < 0) {
192 ldout(cct, 0) << "ERROR: parse_list for read returned r="
193 << r << dendl;
194 return r;
195 }
196
9f95a23c 197 r = add_grants(user_ctl, uids, SWIFT_PERM_READ);
7c673cae
FG
198 if (r < 0) {
199 ldout(cct, 0) << "ERROR: add_grants for read returned r="
200 << r << dendl;
201 return r;
202 }
203 rw_mask |= SWIFT_PERM_READ;
204 }
28e407b8 205 if (write_list) {
7c673cae
FG
206 std::vector<std::string> uids;
207 int r = parse_list(write_list, uids);
208 if (r < 0) {
209 ldout(cct, 0) << "ERROR: parse_list for write returned r="
210 << r << dendl;
211 return r;
212 }
213
9f95a23c 214 r = add_grants(user_ctl, uids, SWIFT_PERM_WRITE);
7c673cae
FG
215 if (r < 0) {
216 ldout(cct, 0) << "ERROR: add_grants for write returned r="
217 << r << dendl;
218 return r;
219 }
220 rw_mask |= SWIFT_PERM_WRITE;
221 }
222 return 0;
223}
224
225void RGWAccessControlPolicy_SWIFT::filter_merge(uint32_t rw_mask,
226 RGWAccessControlPolicy_SWIFT *old)
227{
228 /* rw_mask&SWIFT_PERM_READ => setting read acl,
229 * rw_mask&SWIFT_PERM_WRITE => setting write acl
230 * when bit is cleared, copy matching elements from old.
231 */
232 if (rw_mask == (SWIFT_PERM_READ|SWIFT_PERM_WRITE)) {
233 return;
234 }
235 rw_mask ^= (SWIFT_PERM_READ|SWIFT_PERM_WRITE);
236 for (auto &iter: old->acl.get_grant_map()) {
237 ACLGrant& grant = iter.second;
238 uint32_t perm = grant.get_permission().get_permissions();
239 rgw_user id;
240 string url_spec;
241 if (!grant.get_id(id)) {
242 if (grant.get_group() != ACL_GROUP_ALL_USERS) {
243 url_spec = grant.get_referer();
244 if (url_spec.empty()) {
245 continue;
246 }
247 if (perm == 0) {
248 /* We need to carry also negative, HTTP referrer-based ACLs. */
249 perm = SWIFT_PERM_READ;
250 }
251 }
252 }
253 if (perm & rw_mask) {
254 acl.add_grant(&grant);
255 }
256 }
257}
258
259void RGWAccessControlPolicy_SWIFT::to_str(string& read, string& write)
260{
261 multimap<string, ACLGrant>& m = acl.get_grant_map();
262 multimap<string, ACLGrant>::iterator iter;
263
264 for (iter = m.begin(); iter != m.end(); ++iter) {
265 ACLGrant& grant = iter->second;
266 const uint32_t perm = grant.get_permission().get_permissions();
267 rgw_user id;
268 string url_spec;
269 if (!grant.get_id(id)) {
270 if (grant.get_group() == ACL_GROUP_ALL_USERS) {
271 id = SWIFT_GROUP_ALL_USERS;
272 } else {
273 url_spec = grant.get_referer();
274 if (url_spec.empty()) {
275 continue;
276 }
277 id = (perm != 0) ? ".r:" + url_spec : ".r:-" + url_spec;
278 }
279 }
280 if (perm & SWIFT_PERM_READ) {
281 if (!read.empty()) {
282 read.append(",");
283 }
284 read.append(id.to_str());
285 } else if (perm & SWIFT_PERM_WRITE) {
286 if (!write.empty()) {
287 write.append(",");
288 }
289 write.append(id.to_str());
290 } else if (perm == 0 && !url_spec.empty()) {
291 /* only X-Container-Read headers support referers */
292 if (!read.empty()) {
293 read.append(",");
294 }
295 read.append(id.to_str());
296 }
297 }
298}
299
9f95a23c 300void RGWAccessControlPolicy_SWIFTAcct::add_grants(RGWUserCtl * const user_ctl,
7c673cae
FG
301 const std::vector<std::string>& uids,
302 const uint32_t perm)
303{
304 for (const auto& uid : uids) {
305 ACLGrant grant;
306 RGWUserInfo grant_user;
307
308 if (uid_is_public(uid)) {
309 grant.set_group(ACL_GROUP_ALL_USERS, perm);
310 acl.add_grant(&grant);
311 } else {
312 rgw_user user(uid);
313
9f95a23c 314 if (user_ctl->get_info_by_uid(user, &grant_user, null_yield) < 0) {
7c673cae
FG
315 ldout(cct, 10) << "grant user does not exist:" << uid << dendl;
316 /* skipping silently */
317 grant.set_canon(user, std::string(), perm);
318 acl.add_grant(&grant);
319 } else {
320 grant.set_canon(user, grant_user.display_name, perm);
321 acl.add_grant(&grant);
322 }
323 }
324 }
325}
326
9f95a23c 327bool RGWAccessControlPolicy_SWIFTAcct::create(RGWUserCtl * const user_ctl,
7c673cae
FG
328 const rgw_user& id,
329 const std::string& name,
330 const std::string& acl_str)
331{
332 acl.create_default(id, name);
333 owner.set_id(id);
334 owner.set_name(name);
335
336 JSONParser parser;
337
338 if (!parser.parse(acl_str.c_str(), acl_str.length())) {
339 ldout(cct, 0) << "ERROR: JSONParser::parse returned error=" << dendl;
340 return false;
341 }
342
343 JSONObjIter iter = parser.find_first("admin");
344 if (!iter.end() && (*iter)->is_array()) {
345 std::vector<std::string> admin;
346 decode_json_obj(admin, *iter);
347 ldout(cct, 0) << "admins: " << admin << dendl;
348
9f95a23c 349 add_grants(user_ctl, admin, SWIFT_PERM_ADMIN);
7c673cae
FG
350 }
351
352 iter = parser.find_first("read-write");
353 if (!iter.end() && (*iter)->is_array()) {
354 std::vector<std::string> readwrite;
355 decode_json_obj(readwrite, *iter);
356 ldout(cct, 0) << "read-write: " << readwrite << dendl;
357
9f95a23c 358 add_grants(user_ctl, readwrite, SWIFT_PERM_RWRT);
7c673cae
FG
359 }
360
361 iter = parser.find_first("read-only");
362 if (!iter.end() && (*iter)->is_array()) {
363 std::vector<std::string> readonly;
364 decode_json_obj(readonly, *iter);
365 ldout(cct, 0) << "read-only: " << readonly << dendl;
366
9f95a23c 367 add_grants(user_ctl, readonly, SWIFT_PERM_READ);
7c673cae
FG
368 }
369
370 return true;
371}
372
224ce89b 373boost::optional<std::string> RGWAccessControlPolicy_SWIFTAcct::to_str() const
7c673cae
FG
374{
375 std::vector<std::string> admin;
376 std::vector<std::string> readwrite;
377 std::vector<std::string> readonly;
378
379 /* Parition the grant map into three not-overlapping groups. */
380 for (const auto& item : get_acl().get_grant_map()) {
381 const ACLGrant& grant = item.second;
382 const uint32_t perm = grant.get_permission().get_permissions();
383
384 rgw_user id;
385 if (!grant.get_id(id)) {
386 if (grant.get_group() != ACL_GROUP_ALL_USERS) {
387 continue;
388 }
389 id = SWIFT_GROUP_ALL_USERS;
390 } else if (owner.get_id() == id) {
391 continue;
392 }
393
394 if (SWIFT_PERM_ADMIN == (perm & SWIFT_PERM_ADMIN)) {
395 admin.insert(admin.end(), id.to_str());
396 } else if (SWIFT_PERM_RWRT == (perm & SWIFT_PERM_RWRT)) {
397 readwrite.insert(readwrite.end(), id.to_str());
398 } else if (SWIFT_PERM_READ == (perm & SWIFT_PERM_READ)) {
399 readonly.insert(readonly.end(), id.to_str());
400 } else {
401 // FIXME: print a warning
402 }
403 }
404
224ce89b
WB
405 /* If there is no grant to serialize, let's exit earlier to not return
406 * an empty JSON object which brakes the functional tests of Swift. */
407 if (admin.empty() && readwrite.empty() && readonly.empty()) {
408 return boost::none;
409 }
410
7c673cae
FG
411 /* Serialize the groups. */
412 JSONFormatter formatter;
413
414 formatter.open_object_section("acl");
415 if (!readonly.empty()) {
416 encode_json("read-only", readonly, &formatter);
417 }
418 if (!readwrite.empty()) {
419 encode_json("read-write", readwrite, &formatter);
420 }
421 if (!admin.empty()) {
422 encode_json("admin", admin, &formatter);
423 }
424 formatter.close_section();
425
426 std::ostringstream oss;
427 formatter.flush(oss);
428
224ce89b 429 return oss.str();
7c673cae 430}