]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_acl_swift.cc
add subtree-ish sources for 12.0.3
[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
27static 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
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
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);
97 } else {
98 if ('*' == url_spec[0]) {
99 url_spec = url_spec.substr(1);
100 boost::algorithm::trim(url_spec);
101 }
102
103 if (url_spec.empty() || url_spec == ".") {
104 return boost::none;
105 }
106
107 grant.set_referer(url_spec, is_negative ? 0 : perm);
108 }
109
110 return grant;
111 } catch (std::out_of_range) {
112 return boost::none;
113 }
114}
115
116static ACLGrant user_to_grant(CephContext* const cct,
117 RGWRados* const store,
118 const std::string& uid,
119 const uint32_t perm)
120{
121 rgw_user user(uid);
122 RGWUserInfo grant_user;
123 ACLGrant grant;
124
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);
129 } else {
130 grant.set_canon(user, grant_user.display_name, perm);
131 }
132
133 return grant;
134}
135
136int RGWAccessControlPolicy_SWIFT::add_grants(RGWRados* const store,
137 const std::vector<std::string>& uids,
138 const uint32_t perm)
139{
140 for (const auto& uid : uids) {
141 boost::optional<ACLGrant> grant;
142 ldout(cct, 20) << "trying to add grant for ACL uid=" << uid << dendl;
143
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);
150 } else {
151 /* Yes, *potentially* an HTTP referral. */
152 auto designator = uid.substr(0, pos);
153 auto designatee = uid.substr(pos + 1);
154
155 /* Swift strips whitespaces at both beginning and end. */
156 boost::algorithm::trim(designator);
157 boost::algorithm::trim(designatee);
158
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);
164 }
165 }
166
167 if (grant) {
168 acl.add_grant(&*grant);
169 } else {
170 return -EINVAL;
171 }
172 }
173
174 return 0;
175}
176
177
178int RGWAccessControlPolicy_SWIFT::create(RGWRados* const store,
179 const rgw_user& id,
180 const std::string& name,
181 const std::string& read_list,
182 const std::string& write_list,
183 uint32_t& rw_mask)
184{
185 acl.create_default(id, name);
186 owner.set_id(id);
187 owner.set_name(name);
188 rw_mask = 0;
189
190 if (read_list.size()) {
191 std::vector<std::string> uids;
192 int r = parse_list(read_list, uids);
193 if (r < 0) {
194 ldout(cct, 0) << "ERROR: parse_list for read returned r="
195 << r << dendl;
196 return r;
197 }
198
199 r = add_grants(store, uids, SWIFT_PERM_READ);
200 if (r < 0) {
201 ldout(cct, 0) << "ERROR: add_grants for read returned r="
202 << r << dendl;
203 return r;
204 }
205 rw_mask |= SWIFT_PERM_READ;
206 }
207 if (write_list.size()) {
208 std::vector<std::string> uids;
209 int r = parse_list(write_list, uids);
210 if (r < 0) {
211 ldout(cct, 0) << "ERROR: parse_list for write returned r="
212 << r << dendl;
213 return r;
214 }
215
216 r = add_grants(store, uids, SWIFT_PERM_WRITE);
217 if (r < 0) {
218 ldout(cct, 0) << "ERROR: add_grants for write returned r="
219 << r << dendl;
220 return r;
221 }
222 rw_mask |= SWIFT_PERM_WRITE;
223 }
224 return 0;
225}
226
227void RGWAccessControlPolicy_SWIFT::filter_merge(uint32_t rw_mask,
228 RGWAccessControlPolicy_SWIFT *old)
229{
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.
233 */
234 if (rw_mask == (SWIFT_PERM_READ|SWIFT_PERM_WRITE)) {
235 return;
236 }
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();
241 rgw_user id;
242 string url_spec;
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()) {
247 continue;
248 }
249 if (perm == 0) {
250 /* We need to carry also negative, HTTP referrer-based ACLs. */
251 perm = SWIFT_PERM_READ;
252 }
253 }
254 }
255 if (perm & rw_mask) {
256 acl.add_grant(&grant);
257 }
258 }
259}
260
261void RGWAccessControlPolicy_SWIFT::to_str(string& read, string& write)
262{
263 multimap<string, ACLGrant>& m = acl.get_grant_map();
264 multimap<string, ACLGrant>::iterator iter;
265
266 for (iter = m.begin(); iter != m.end(); ++iter) {
267 ACLGrant& grant = iter->second;
268 const uint32_t perm = grant.get_permission().get_permissions();
269 rgw_user id;
270 string url_spec;
271 if (!grant.get_id(id)) {
272 if (grant.get_group() == ACL_GROUP_ALL_USERS) {
273 id = SWIFT_GROUP_ALL_USERS;
274 } else {
275 url_spec = grant.get_referer();
276 if (url_spec.empty()) {
277 continue;
278 }
279 id = (perm != 0) ? ".r:" + url_spec : ".r:-" + url_spec;
280 }
281 }
282 if (perm & SWIFT_PERM_READ) {
283 if (!read.empty()) {
284 read.append(",");
285 }
286 read.append(id.to_str());
287 } else if (perm & SWIFT_PERM_WRITE) {
288 if (!write.empty()) {
289 write.append(",");
290 }
291 write.append(id.to_str());
292 } else if (perm == 0 && !url_spec.empty()) {
293 /* only X-Container-Read headers support referers */
294 if (!read.empty()) {
295 read.append(",");
296 }
297 read.append(id.to_str());
298 }
299 }
300}
301
302void RGWAccessControlPolicy_SWIFTAcct::add_grants(RGWRados * const store,
303 const std::vector<std::string>& uids,
304 const uint32_t perm)
305{
306 for (const auto& uid : uids) {
307 ACLGrant grant;
308 RGWUserInfo grant_user;
309
310 if (uid_is_public(uid)) {
311 grant.set_group(ACL_GROUP_ALL_USERS, perm);
312 acl.add_grant(&grant);
313 } else {
314 rgw_user user(uid);
315
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);
321 } else {
322 grant.set_canon(user, grant_user.display_name, perm);
323 acl.add_grant(&grant);
324 }
325 }
326 }
327}
328
329bool RGWAccessControlPolicy_SWIFTAcct::create(RGWRados * const store,
330 const rgw_user& id,
331 const std::string& name,
332 const std::string& acl_str)
333{
334 acl.create_default(id, name);
335 owner.set_id(id);
336 owner.set_name(name);
337
338 JSONParser parser;
339
340 if (!parser.parse(acl_str.c_str(), acl_str.length())) {
341 ldout(cct, 0) << "ERROR: JSONParser::parse returned error=" << dendl;
342 return false;
343 }
344
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;
350
351 add_grants(store, admin, SWIFT_PERM_ADMIN);
352 }
353
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;
359
360 add_grants(store, readwrite, SWIFT_PERM_RWRT);
361 }
362
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;
368
369 add_grants(store, readonly, SWIFT_PERM_READ);
370 }
371
372 return true;
373}
374
375void RGWAccessControlPolicy_SWIFTAcct::to_str(std::string& acl_str) const
376{
377 std::vector<std::string> admin;
378 std::vector<std::string> readwrite;
379 std::vector<std::string> readonly;
380
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();
385
386 rgw_user id;
387 if (!grant.get_id(id)) {
388 if (grant.get_group() != ACL_GROUP_ALL_USERS) {
389 continue;
390 }
391 id = SWIFT_GROUP_ALL_USERS;
392 } else if (owner.get_id() == id) {
393 continue;
394 }
395
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());
402 } else {
403 // FIXME: print a warning
404 }
405 }
406
407 /* Serialize the groups. */
408 JSONFormatter formatter;
409
410 formatter.open_object_section("acl");
411 if (!readonly.empty()) {
412 encode_json("read-only", readonly, &formatter);
413 }
414 if (!readwrite.empty()) {
415 encode_json("read-write", readwrite, &formatter);
416 }
417 if (!admin.empty()) {
418 encode_json("admin", admin, &formatter);
419 }
420 formatter.close_section();
421
422 std::ostringstream oss;
423 formatter.flush(oss);
424
425 acl_str = oss.str();
426}