]>
Commit | Line | Data |
---|---|---|
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 | #ifndef CEPH_RGW_ACL_H | |
5 | #define CEPH_RGW_ACL_H | |
6 | ||
7 | #include <map> | |
8 | #include <string> | |
9 | #include <include/types.h> | |
10 | ||
11 | #include <boost/optional.hpp> | |
12 | #include <boost/utility/string_ref.hpp> | |
13 | ||
14 | #include "common/debug.h" | |
15 | ||
16 | #include "rgw_basic_types.h" | |
17 | ||
7c673cae FG |
18 | #define RGW_PERM_NONE 0x00 |
19 | #define RGW_PERM_READ 0x01 | |
20 | #define RGW_PERM_WRITE 0x02 | |
21 | #define RGW_PERM_READ_ACP 0x04 | |
22 | #define RGW_PERM_WRITE_ACP 0x08 | |
23 | #define RGW_PERM_READ_OBJS 0x10 | |
24 | #define RGW_PERM_WRITE_OBJS 0x20 | |
25 | #define RGW_PERM_FULL_CONTROL ( RGW_PERM_READ | RGW_PERM_WRITE | \ | |
26 | RGW_PERM_READ_ACP | RGW_PERM_WRITE_ACP ) | |
27 | #define RGW_PERM_ALL_S3 RGW_PERM_FULL_CONTROL | |
28 | #define RGW_PERM_INVALID 0xFF00 | |
29 | ||
31f18b77 FG |
30 | static constexpr char RGW_REFERER_WILDCARD[] = "*"; |
31 | ||
7c673cae FG |
32 | enum ACLGranteeTypeEnum { |
33 | /* numbers are encoded, should not change */ | |
34 | ACL_TYPE_CANON_USER = 0, | |
35 | ACL_TYPE_EMAIL_USER = 1, | |
36 | ACL_TYPE_GROUP = 2, | |
37 | ACL_TYPE_UNKNOWN = 3, | |
38 | ACL_TYPE_REFERER = 4, | |
39 | }; | |
40 | ||
41 | enum ACLGroupTypeEnum { | |
42 | /* numbers are encoded should not change */ | |
43 | ACL_GROUP_NONE = 0, | |
44 | ACL_GROUP_ALL_USERS = 1, | |
45 | ACL_GROUP_AUTHENTICATED_USERS = 2, | |
46 | }; | |
47 | ||
48 | class ACLPermission | |
49 | { | |
50 | protected: | |
51 | int flags; | |
52 | public: | |
53 | ACLPermission() : flags(0) {} | |
54 | ~ACLPermission() {} | |
55 | uint32_t get_permissions() const { return flags; } | |
56 | void set_permissions(uint32_t perm) { flags = perm; } | |
57 | ||
58 | void encode(bufferlist& bl) const { | |
59 | ENCODE_START(2, 2, bl); | |
60 | ::encode(flags, bl); | |
61 | ENCODE_FINISH(bl); | |
62 | } | |
63 | void decode(bufferlist::iterator& bl) { | |
64 | DECODE_START_LEGACY_COMPAT_LEN(2, 2, 2, bl); | |
65 | ::decode(flags, bl); | |
66 | DECODE_FINISH(bl); | |
67 | } | |
68 | void dump(Formatter *f) const; | |
69 | static void generate_test_instances(list<ACLPermission*>& o); | |
70 | }; | |
71 | WRITE_CLASS_ENCODER(ACLPermission) | |
72 | ||
73 | class ACLGranteeType | |
74 | { | |
75 | protected: | |
76 | __u32 type; | |
77 | public: | |
78 | ACLGranteeType() : type(ACL_TYPE_UNKNOWN) {} | |
79 | virtual ~ACLGranteeType() {} | |
80 | // virtual const char *to_string() = 0; | |
81 | ACLGranteeTypeEnum get_type() const { return (ACLGranteeTypeEnum)type; } | |
82 | void set(ACLGranteeTypeEnum t) { type = t; } | |
83 | // virtual void set(const char *s) = 0; | |
84 | void encode(bufferlist& bl) const { | |
85 | ENCODE_START(2, 2, bl); | |
86 | ::encode(type, bl); | |
87 | ENCODE_FINISH(bl); | |
88 | } | |
89 | void decode(bufferlist::iterator& bl) { | |
90 | DECODE_START_LEGACY_COMPAT_LEN(2, 2, 2, bl); | |
91 | ::decode(type, bl); | |
92 | DECODE_FINISH(bl); | |
93 | } | |
94 | void dump(Formatter *f) const; | |
95 | static void generate_test_instances(list<ACLGranteeType*>& o); | |
96 | }; | |
97 | WRITE_CLASS_ENCODER(ACLGranteeType) | |
98 | ||
99 | class ACLGrantee | |
100 | { | |
101 | public: | |
102 | ACLGrantee() {} | |
103 | ~ACLGrantee() {} | |
104 | }; | |
105 | ||
106 | ||
107 | class ACLGrant | |
108 | { | |
109 | protected: | |
110 | ACLGranteeType type; | |
111 | rgw_user id; | |
112 | string email; | |
113 | ACLPermission permission; | |
114 | string name; | |
115 | ACLGroupTypeEnum group; | |
116 | string url_spec; | |
117 | ||
118 | public: | |
119 | ACLGrant() : group(ACL_GROUP_NONE) {} | |
120 | virtual ~ACLGrant() {} | |
121 | ||
122 | /* there's an assumption here that email/uri/id encodings are | |
123 | different and there can't be any overlap */ | |
124 | bool get_id(rgw_user& _id) const { | |
125 | switch(type.get_type()) { | |
126 | case ACL_TYPE_EMAIL_USER: | |
127 | _id = email; // implies from_str() that parses the 't:u' syntax | |
128 | return true; | |
129 | case ACL_TYPE_GROUP: | |
130 | case ACL_TYPE_REFERER: | |
131 | return false; | |
132 | default: | |
133 | _id = id; | |
134 | return true; | |
135 | } | |
136 | } | |
137 | ACLGranteeType& get_type() { return type; } | |
138 | const ACLGranteeType& get_type() const { return type; } | |
139 | ACLPermission& get_permission() { return permission; } | |
140 | const ACLPermission& get_permission() const { return permission; } | |
141 | ACLGroupTypeEnum get_group() const { return group; } | |
142 | const string& get_referer() const { return url_spec; } | |
143 | ||
144 | void encode(bufferlist& bl) const { | |
145 | ENCODE_START(5, 3, bl); | |
146 | ::encode(type, bl); | |
147 | string s; | |
148 | id.to_str(s); | |
149 | ::encode(s, bl); | |
150 | string uri; | |
151 | ::encode(uri, bl); | |
152 | ::encode(email, bl); | |
153 | ::encode(permission, bl); | |
154 | ::encode(name, bl); | |
155 | __u32 g = (__u32)group; | |
156 | ::encode(g, bl); | |
157 | ::encode(url_spec, bl); | |
158 | ENCODE_FINISH(bl); | |
159 | } | |
160 | void decode(bufferlist::iterator& bl) { | |
161 | DECODE_START_LEGACY_COMPAT_LEN(5, 3, 3, bl); | |
162 | ::decode(type, bl); | |
163 | string s; | |
164 | ::decode(s, bl); | |
165 | id.from_str(s); | |
166 | string uri; | |
167 | ::decode(uri, bl); | |
168 | ::decode(email, bl); | |
169 | ::decode(permission, bl); | |
170 | ::decode(name, bl); | |
171 | if (struct_v > 1) { | |
172 | __u32 g; | |
173 | ::decode(g, bl); | |
174 | group = (ACLGroupTypeEnum)g; | |
175 | } else { | |
176 | group = uri_to_group(uri); | |
177 | } | |
178 | if (struct_v >= 5) { | |
179 | ::decode(url_spec, bl); | |
180 | } else { | |
181 | url_spec.clear(); | |
182 | } | |
183 | DECODE_FINISH(bl); | |
184 | } | |
185 | void dump(Formatter *f) const; | |
186 | static void generate_test_instances(list<ACLGrant*>& o); | |
187 | ||
188 | ACLGroupTypeEnum uri_to_group(string& uri); | |
189 | ||
190 | void set_canon(const rgw_user& _id, const string& _name, const uint32_t perm) { | |
191 | type.set(ACL_TYPE_CANON_USER); | |
192 | id = _id; | |
193 | name = _name; | |
194 | permission.set_permissions(perm); | |
195 | } | |
196 | void set_group(ACLGroupTypeEnum _group, const uint32_t perm) { | |
197 | type.set(ACL_TYPE_GROUP); | |
198 | group = _group; | |
199 | permission.set_permissions(perm); | |
200 | } | |
201 | void set_referer(const std::string& _url_spec, const uint32_t perm) { | |
202 | type.set(ACL_TYPE_REFERER); | |
203 | url_spec = _url_spec; | |
204 | permission.set_permissions(perm); | |
205 | } | |
206 | }; | |
207 | WRITE_CLASS_ENCODER(ACLGrant) | |
208 | ||
209 | struct ACLReferer { | |
210 | std::string url_spec; | |
211 | uint32_t perm; | |
212 | ||
213 | ACLReferer() : perm(0) {} | |
214 | ACLReferer(const std::string& url_spec, | |
215 | const uint32_t perm) | |
216 | : url_spec(url_spec), | |
217 | perm(perm) { | |
218 | } | |
219 | ||
220 | bool is_match(boost::string_ref http_referer) const { | |
221 | const auto http_host = get_http_host(http_referer); | |
222 | if (!http_host || http_host->length() < url_spec.length()) { | |
223 | return false; | |
224 | } | |
225 | ||
31f18b77 FG |
226 | if ("*" == url_spec) { |
227 | return true; | |
228 | } | |
229 | ||
7c673cae FG |
230 | if (http_host->compare(url_spec) == 0) { |
231 | return true; | |
232 | } | |
233 | ||
234 | if ('.' == url_spec[0]) { | |
235 | /* Wildcard support: a referer matches the spec when its last char are | |
236 | * perfectly equal to spec. */ | |
237 | return http_host->ends_with(url_spec); | |
238 | } | |
239 | ||
240 | return false; | |
241 | } | |
242 | ||
243 | void encode(bufferlist& bl) const { | |
244 | ENCODE_START(1, 1, bl); | |
245 | ::encode(url_spec, bl); | |
246 | ::encode(perm, bl); | |
247 | ENCODE_FINISH(bl); | |
248 | } | |
249 | void decode(bufferlist::iterator& bl) { | |
250 | DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); | |
251 | ::decode(url_spec, bl); | |
252 | ::decode(perm, bl); | |
253 | DECODE_FINISH(bl); | |
254 | } | |
255 | void dump(Formatter *f) const; | |
256 | ||
257 | private: | |
258 | boost::optional<boost::string_ref> get_http_host(const boost::string_ref url) const { | |
259 | size_t pos = url.find("://"); | |
260 | if (pos == boost::string_ref::npos || url.starts_with("://") || | |
261 | url.ends_with("://") || url.ends_with('@')) { | |
262 | return boost::none; | |
263 | } | |
264 | boost::string_ref url_sub = url.substr(pos + strlen("://")); | |
265 | pos = url_sub.find('@'); | |
266 | if (pos != boost::string_ref::npos) { | |
267 | url_sub = url_sub.substr(pos + 1); | |
268 | } | |
269 | pos = url_sub.find_first_of("/:"); | |
270 | if (pos == boost::string_ref::npos) { | |
271 | /* no port or path exists */ | |
272 | return url_sub; | |
273 | } | |
274 | return url_sub.substr(0, pos); | |
275 | } | |
276 | }; | |
277 | WRITE_CLASS_ENCODER(ACLReferer) | |
278 | ||
279 | namespace rgw { | |
280 | namespace auth { | |
281 | class Identity; | |
282 | } | |
283 | } | |
284 | ||
285 | class RGWAccessControlList | |
286 | { | |
287 | protected: | |
288 | CephContext *cct; | |
289 | /* FIXME: in the feature we should consider switching to uint32_t also | |
290 | * in data structures. */ | |
291 | map<string, int> acl_user_map; | |
292 | map<uint32_t, int> acl_group_map; | |
293 | list<ACLReferer> referer_list; | |
294 | multimap<string, ACLGrant> grant_map; | |
295 | void _add_grant(ACLGrant *grant); | |
296 | public: | |
297 | explicit RGWAccessControlList(CephContext *_cct) : cct(_cct) {} | |
298 | RGWAccessControlList() : cct(NULL) {} | |
299 | ||
300 | void set_ctx(CephContext *ctx) { | |
301 | cct = ctx; | |
302 | } | |
303 | ||
304 | virtual ~RGWAccessControlList() {} | |
305 | ||
306 | uint32_t get_perm(const rgw::auth::Identity& auth_identity, | |
307 | uint32_t perm_mask); | |
308 | uint32_t get_group_perm(ACLGroupTypeEnum group, uint32_t perm_mask); | |
31f18b77 FG |
309 | uint32_t get_referer_perm(uint32_t current_perm, |
310 | std::string http_referer, | |
311 | uint32_t perm_mask); | |
7c673cae FG |
312 | void encode(bufferlist& bl) const { |
313 | ENCODE_START(4, 3, bl); | |
314 | bool maps_initialized = true; | |
315 | ::encode(maps_initialized, bl); | |
316 | ::encode(acl_user_map, bl); | |
317 | ::encode(grant_map, bl); | |
318 | ::encode(acl_group_map, bl); | |
319 | ::encode(referer_list, bl); | |
320 | ENCODE_FINISH(bl); | |
321 | } | |
322 | void decode(bufferlist::iterator& bl) { | |
323 | DECODE_START_LEGACY_COMPAT_LEN(4, 3, 3, bl); | |
324 | bool maps_initialized; | |
325 | ::decode(maps_initialized, bl); | |
326 | ::decode(acl_user_map, bl); | |
327 | ::decode(grant_map, bl); | |
328 | if (struct_v >= 2) { | |
329 | ::decode(acl_group_map, bl); | |
330 | } else if (!maps_initialized) { | |
331 | multimap<string, ACLGrant>::iterator iter; | |
332 | for (iter = grant_map.begin(); iter != grant_map.end(); ++iter) { | |
333 | ACLGrant& grant = iter->second; | |
334 | _add_grant(&grant); | |
335 | } | |
336 | } | |
337 | if (struct_v >= 4) { | |
338 | ::decode(referer_list, bl); | |
339 | } | |
340 | DECODE_FINISH(bl); | |
341 | } | |
342 | void dump(Formatter *f) const; | |
343 | static void generate_test_instances(list<RGWAccessControlList*>& o); | |
344 | ||
345 | void add_grant(ACLGrant *grant); | |
346 | ||
347 | multimap<string, ACLGrant>& get_grant_map() { return grant_map; } | |
348 | const multimap<string, ACLGrant>& get_grant_map() const { return grant_map; } | |
349 | ||
350 | void create_default(const rgw_user& id, string name) { | |
351 | acl_user_map.clear(); | |
352 | acl_group_map.clear(); | |
353 | referer_list.clear(); | |
354 | ||
355 | ACLGrant grant; | |
356 | grant.set_canon(id, name, RGW_PERM_FULL_CONTROL); | |
357 | add_grant(&grant); | |
358 | } | |
359 | }; | |
360 | WRITE_CLASS_ENCODER(RGWAccessControlList) | |
361 | ||
362 | class ACLOwner | |
363 | { | |
364 | protected: | |
365 | rgw_user id; | |
366 | string display_name; | |
367 | public: | |
368 | ACLOwner() {} | |
369 | ~ACLOwner() {} | |
370 | ||
371 | void encode(bufferlist& bl) const { | |
372 | ENCODE_START(3, 2, bl); | |
373 | string s; | |
374 | id.to_str(s); | |
375 | ::encode(s, bl); | |
376 | ::encode(display_name, bl); | |
377 | ENCODE_FINISH(bl); | |
378 | } | |
379 | void decode(bufferlist::iterator& bl) { | |
380 | DECODE_START_LEGACY_COMPAT_LEN(3, 2, 2, bl); | |
381 | string s; | |
382 | ::decode(s, bl); | |
383 | id.from_str(s); | |
384 | ::decode(display_name, bl); | |
385 | DECODE_FINISH(bl); | |
386 | } | |
387 | void dump(Formatter *f) const; | |
31f18b77 | 388 | void decode_json(JSONObj *obj); |
7c673cae FG |
389 | static void generate_test_instances(list<ACLOwner*>& o); |
390 | void set_id(const rgw_user& _id) { id = _id; } | |
391 | void set_name(const string& name) { display_name = name; } | |
392 | ||
393 | rgw_user& get_id() { return id; } | |
394 | const rgw_user& get_id() const { return id; } | |
395 | string& get_display_name() { return display_name; } | |
396 | }; | |
397 | WRITE_CLASS_ENCODER(ACLOwner) | |
398 | ||
399 | class RGWAccessControlPolicy | |
400 | { | |
401 | protected: | |
402 | CephContext *cct; | |
403 | RGWAccessControlList acl; | |
404 | ACLOwner owner; | |
405 | ||
406 | public: | |
407 | explicit RGWAccessControlPolicy(CephContext *_cct) : cct(_cct), acl(_cct) {} | |
408 | RGWAccessControlPolicy() : cct(NULL), acl(NULL) {} | |
409 | virtual ~RGWAccessControlPolicy() {} | |
410 | ||
411 | void set_ctx(CephContext *ctx) { | |
412 | cct = ctx; | |
413 | acl.set_ctx(ctx); | |
414 | } | |
415 | ||
416 | uint32_t get_perm(const rgw::auth::Identity& auth_identity, | |
417 | uint32_t perm_mask, | |
418 | const char * http_referer); | |
419 | uint32_t get_group_perm(ACLGroupTypeEnum group, uint32_t perm_mask); | |
420 | bool verify_permission(const rgw::auth::Identity& auth_identity, | |
421 | uint32_t user_perm_mask, | |
422 | uint32_t perm, | |
423 | const char * http_referer = nullptr); | |
424 | ||
425 | void encode(bufferlist& bl) const { | |
426 | ENCODE_START(2, 2, bl); | |
427 | ::encode(owner, bl); | |
428 | ::encode(acl, bl); | |
429 | ENCODE_FINISH(bl); | |
430 | } | |
431 | void decode(bufferlist::iterator& bl) { | |
432 | DECODE_START_LEGACY_COMPAT_LEN(2, 2, 2, bl); | |
433 | ::decode(owner, bl); | |
434 | ::decode(acl, bl); | |
435 | DECODE_FINISH(bl); | |
436 | } | |
437 | void dump(Formatter *f) const; | |
438 | static void generate_test_instances(list<RGWAccessControlPolicy*>& o); | |
439 | void decode_owner(bufferlist::iterator& bl) { // sometimes we only need that, should be faster | |
440 | DECODE_START_LEGACY_COMPAT_LEN(2, 2, 2, bl); | |
441 | ::decode(owner, bl); | |
442 | DECODE_FINISH(bl); | |
443 | } | |
444 | ||
445 | void set_owner(ACLOwner& o) { owner = o; } | |
446 | ACLOwner& get_owner() { | |
447 | return owner; | |
448 | } | |
449 | ||
450 | void create_default(const rgw_user& id, string& name) { | |
451 | acl.create_default(id, name); | |
452 | owner.set_id(id); | |
453 | owner.set_name(name); | |
454 | } | |
455 | RGWAccessControlList& get_acl() { | |
456 | return acl; | |
457 | } | |
458 | const RGWAccessControlList& get_acl() const { | |
459 | return acl; | |
460 | } | |
461 | ||
462 | virtual bool compare_group_name(string& id, ACLGroupTypeEnum group) { return false; } | |
463 | }; | |
464 | WRITE_CLASS_ENCODER(RGWAccessControlPolicy) | |
465 | ||
466 | #endif |