Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / osd / OSDCap.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2009-2011 New Dream Network
7  *
8  * This is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License version 2.1, as published by the Free Software
11  * Foundation.  See file COPYING.
12  *
13  */
14
15 #include <boost/config/warning_disable.hpp>
16 #include <boost/spirit/include/qi.hpp>
17 #include <boost/spirit/include/phoenix_operator.hpp>
18 #include <boost/spirit/include/phoenix.hpp>
19
20 #include "OSDCap.h"
21 #include "common/config.h"
22 #include "common/debug.h"
23
24 using std::ostream;
25 using std::vector;
26
27 ostream& operator<<(ostream& out, const osd_rwxa_t& p)
28 {
29   if (p == OSD_CAP_ANY)
30     return out << "*";
31
32   if (p & OSD_CAP_R)
33     out << "r";
34   if (p & OSD_CAP_W)
35     out << "w";
36   if ((p & OSD_CAP_X) == OSD_CAP_X) {
37     out << "x";
38   } else {
39     if (p & OSD_CAP_CLS_R)
40       out << " class-read";
41     if (p & OSD_CAP_CLS_W)
42       out << " class-write";
43   }
44   return out;
45 }
46
47 ostream& operator<<(ostream& out, const OSDCapSpec& s)
48 {
49   if (s.allow)
50     return out << s.allow;
51   if (s.class_name.length())
52     return out << "class '" << s.class_name << "' '" << s.class_allow << "'";
53   return out;
54 }
55
56 ostream& operator<<(ostream& out, const OSDCapPoolNamespace& pns)
57 {
58   if (!pns.pool_name.empty()) {
59     out << "pool " << pns.pool_name << " ";
60   }
61   if (pns.nspace) {
62     out << "namespace ";
63     if (pns.nspace->empty()) {
64       out << "\"\"";
65     } else {
66       out << *pns.nspace;
67     }
68     out << " ";
69   }
70   return out;
71 }
72
73 ostream& operator<<(ostream& out, const OSDCapMatch& m)
74 {
75   if (m.auid != -1LL) {
76     out << "auid " << m.auid << " ";
77   } else {
78     out << m.pool_namespace;
79   }
80
81   if (m.object_prefix.length()) {
82     out << "object_prefix " << m.object_prefix << " ";
83   }
84   return out;
85 }
86
87 ostream& operator<<(ostream& out, const OSDCapProfile& m)
88 {
89   out << "profile " << m.name;
90   out << m.pool_namespace;
91   return out;
92 }
93
94 bool OSDCapPoolNamespace::is_match(const std::string& pn,
95                                    const std::string& ns) const
96 {
97   if (!pool_name.empty()) {
98     if (pool_name != pn) {
99       return false;
100     }
101   }
102   if (nspace) {
103     if (*nspace != ns) {
104       return false;
105     }
106   }
107   return true;
108 }
109
110 bool OSDCapPoolNamespace::is_match_all() const
111 {
112   if (!pool_name.empty())
113     return false;
114   if (nspace)
115     return false;
116   return true;
117 }
118
119 bool OSDCapMatch::is_match(const string& pn, const string& ns,
120                            int64_t pool_auid, const string& object) const
121 {
122   if (auid >= 0) {
123     if (auid != pool_auid)
124       return false;
125   } else if (!pool_namespace.is_match(pn, ns)) {
126     return false;
127   }
128
129   if (object_prefix.length()) {
130     if (object.find(object_prefix) != 0)
131       return false;
132   }
133   return true;
134 }
135
136 bool OSDCapMatch::is_match_all() const
137 {
138   if (auid >= 0) {
139     return false;
140   } else if (!pool_namespace.is_match_all()) {
141     return false;
142   }
143
144   if (object_prefix.length()) {
145     return false;
146   }
147   return true;
148 }
149
150 ostream& operator<<(ostream& out, const OSDCapGrant& g)
151 {
152   out << "grant(";
153   if (g.profile.is_valid()) {
154     out << g.profile << " [";
155     for (auto it = g.profile_grants.cbegin();
156          it != g.profile_grants.cend(); ++it) {
157       if (it != g.profile_grants.cbegin()) {
158         out << ",";
159       }
160       out << *it;
161     }
162     out << "]";
163   } else {
164     out << g.match << g.spec;
165   }
166   out << ")";
167   return out;
168 }
169
170 bool OSDCapGrant::allow_all() const
171 {
172   if (profile.is_valid()) {
173     return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
174                        [](const OSDCapGrant& grant) {
175         return grant.allow_all();
176       });
177   }
178
179   return (match.is_match_all() && spec.allow_all());
180 }
181
182 bool OSDCapGrant::is_capable(const string& pool_name, const string& ns,
183                              int64_t pool_auid, const string& object,
184                              bool op_may_read, bool op_may_write,
185                              const std::vector<OpRequest::ClassInfo>& classes,
186                              std::vector<bool>* class_allowed) const
187 {
188   osd_rwxa_t allow = 0;
189   if (profile.is_valid()) {
190     return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
191                        [&](const OSDCapGrant& grant) {
192         return grant.is_capable(pool_name, ns, pool_auid, object, op_may_read,
193                                 op_may_write, classes, class_allowed);
194       });
195   } else {
196     if (match.is_match(pool_name, ns, pool_auid, object)) {
197       allow = allow | spec.allow;
198       if ((op_may_read && !(allow & OSD_CAP_R)) ||
199           (op_may_write && !(allow & OSD_CAP_W))) {
200         return false;
201       }
202       if (!classes.empty()) {
203         // check 'allow *'
204         if (spec.allow_all()) {
205           return true;
206         }
207
208         // compare this grant to each class in the operation
209         for (size_t i = 0; i < classes.size(); ++i) {
210           // check 'allow class foo'
211           if (!spec.class_name.empty() && classes[i].name == spec.class_name) {
212             (*class_allowed)[i] = true;
213             continue;
214           }
215           // check 'allow x | class-{rw}': must be on whitelist
216           if (!classes[i].whitelisted) {
217             continue;
218           }
219           if ((classes[i].read && !(allow & OSD_CAP_CLS_R)) ||
220               (classes[i].write && !(allow & OSD_CAP_CLS_W))) {
221             continue;
222           }
223           (*class_allowed)[i] = true;
224         }
225         if (!std::all_of(class_allowed->cbegin(), class_allowed->cend(),
226               [](bool v) { return v; })) {
227           return false;
228         }
229       }
230       return true;
231     }
232   }
233   return false;
234 }
235
236 void OSDCapGrant::expand_profile()
237 {
238   if (profile.name == "read-only") {
239     // grants READ-ONLY caps to the OSD
240     profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
241                                 OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
242     return;
243   }
244   if (profile.name == "read-write") {
245     // grants READ-WRITE caps to the OSD
246     profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
247                                 OSDCapSpec(osd_rwxa_t(OSD_CAP_R | OSD_CAP_W)));
248   }
249
250   if (profile.name == "rbd") {
251     // RBD read-write grant
252     profile_grants.emplace_back(OSDCapMatch("", "", "rbd_children"),
253                                 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
254     profile_grants.emplace_back(OSDCapMatch("", "", "rbd_mirroring"),
255                                 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
256     profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
257                                 OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
258                                                       OSD_CAP_W |
259                                                       OSD_CAP_X)));
260   }
261   if (profile.name == "rbd-read-only") {
262     // RBD read-only grant
263     profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
264                                 OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
265                                                       OSD_CAP_CLS_R)));
266   }
267 }
268
269 bool OSDCap::allow_all() const
270 {
271   for (auto &grant : grants) {
272     if (grant.allow_all()) {
273       return true;
274     }
275   }
276   return false;
277 }
278
279 void OSDCap::set_allow_all()
280 {
281   grants.clear();
282   grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY)));
283 }
284
285 bool OSDCap::is_capable(const string& pool_name, const string& ns,
286                         int64_t pool_auid, const string& object,
287                         bool op_may_read, bool op_may_write,
288                         const std::vector<OpRequest::ClassInfo>& classes) const
289 {
290   std::vector<bool> class_allowed(classes.size(), false);
291   for (auto &grant : grants) {
292     if (grant.is_capable(pool_name, ns, pool_auid, object, op_may_read,
293                          op_may_write, classes, &class_allowed)) {
294       return true;
295     }
296   }
297   return false;
298 }
299
300
301 // grammar
302 namespace qi = boost::spirit::qi;
303 namespace ascii = boost::spirit::ascii;
304 namespace phoenix = boost::phoenix;
305
306 template <typename Iterator>
307 struct OSDCapParser : qi::grammar<Iterator, OSDCap()>
308 {
309   OSDCapParser() : OSDCapParser::base_type(osdcap)
310   {
311     using qi::char_;
312     using qi::int_;
313     using qi::lexeme;
314     using qi::alnum;
315     using qi::_val;
316     using qi::_1;
317     using qi::_2;
318     using qi::_3;
319     using qi::eps;
320     using qi::lit;
321
322     quoted_string %=
323       lexeme['"' >> +(char_ - '"') >> '"'] | 
324       lexeme['\'' >> +(char_ - '\'') >> '\''];
325     equoted_string %=
326       lexeme['"' >> *(char_ - '"') >> '"'] |
327       lexeme['\'' >> *(char_ - '\'') >> '\''];
328     unquoted_word %= +char_("a-zA-Z0-9_.-");
329     str %= quoted_string | unquoted_word;
330     estr %= equoted_string | unquoted_word;
331
332     spaces = +ascii::space;
333
334     pool_name %= -(spaces >> lit("pool") >> (lit('=') | spaces) >> str);
335     nspace %= (spaces >> lit("namespace") >> (lit('=') | spaces) >> estr);
336
337     // match := [pool[=]<poolname> [namespace[=]<namespace>] | auid <123>] [object_prefix <prefix>]
338     auid %= (spaces >> lit("auid") >> spaces >> int_);
339     object_prefix %= -(spaces >> lit("object_prefix") >> spaces >> str);
340
341     match = (
342       (auid >> object_prefix)                 [_val = phoenix::construct<OSDCapMatch>(_1, _2)] |
343       (pool_name >> nspace >> object_prefix)  [_val = phoenix::construct<OSDCapMatch>(_1, _2, _3)] |
344       (pool_name >> object_prefix)            [_val = phoenix::construct<OSDCapMatch>(_1, _2)]);
345
346     // rwxa := * | [r][w][x] [class-read] [class-write]
347     rwxa =
348       (spaces >> lit("*")[_val = OSD_CAP_ANY]) |
349       ( eps[_val = 0] >>
350         (
351          spaces >>
352          ( lit('r')[_val |= OSD_CAP_R] ||
353            lit('w')[_val |= OSD_CAP_W] ||
354            lit('x')[_val |= OSD_CAP_X] )) ||
355         ( (spaces >> lit("class-read")[_val |= OSD_CAP_CLS_R]) ||
356           (spaces >> lit("class-write")[_val |= OSD_CAP_CLS_W]) ));
357
358     // capspec := * | rwx | class <name> [classcap]
359     class_name %= (spaces >> lit("class") >> spaces >> str);
360     class_cap %= -(spaces >> str);
361     capspec = (
362       (rwxa)                    [_val = phoenix::construct<OSDCapSpec>(_1)] |
363       (class_name >> class_cap) [_val = phoenix::construct<OSDCapSpec>(_1, _2)]);
364
365     // profile := profile <name> [pool[=]<pool> [namespace[=]<namespace>]]
366     profile_name %= (lit("profile") >> (lit('=') | spaces) >> str);
367     profile = (
368       (profile_name >> pool_name >> nspace) [_val = phoenix::construct<OSDCapProfile>(_1, _2, _3)] |
369       (profile_name >> pool_name)           [_val = phoenix::construct<OSDCapProfile>(_1, _2)]);
370
371     // grant := allow match capspec
372     grant = (*ascii::blank >>
373              ((lit("allow") >> capspec >> match)  [_val = phoenix::construct<OSDCapGrant>(_2, _1)] |
374               (lit("allow") >> match >> capspec)  [_val = phoenix::construct<OSDCapGrant>(_1, _2)] |
375               (profile)                           [_val = phoenix::construct<OSDCapGrant>(_1)]
376              ) >> *ascii::blank);
377     // osdcap := grant [grant ...]
378     grants %= (grant % (lit(';') | lit(',')));
379     osdcap = grants  [_val = phoenix::construct<OSDCap>(_1)];
380   }
381   qi::rule<Iterator> spaces;
382   qi::rule<Iterator, unsigned()> rwxa;
383   qi::rule<Iterator, string()> quoted_string, equoted_string;
384   qi::rule<Iterator, string()> unquoted_word;
385   qi::rule<Iterator, string()> str, estr;
386   qi::rule<Iterator, int()> auid;
387   qi::rule<Iterator, string()> class_name;
388   qi::rule<Iterator, string()> class_cap;
389   qi::rule<Iterator, OSDCapSpec()> capspec;
390   qi::rule<Iterator, string()> pool_name;
391   qi::rule<Iterator, string()> nspace;
392   qi::rule<Iterator, string()> object_prefix;
393   qi::rule<Iterator, OSDCapMatch()> match;
394   qi::rule<Iterator, string()> profile_name;
395   qi::rule<Iterator, OSDCapProfile()> profile;
396   qi::rule<Iterator, OSDCapGrant()> grant;
397   qi::rule<Iterator, std::vector<OSDCapGrant>()> grants;
398   qi::rule<Iterator, OSDCap()> osdcap;
399 };
400
401 bool OSDCap::parse(const string& str, ostream *err)
402 {
403   OSDCapParser<string::const_iterator> g;
404   string::const_iterator iter = str.begin();
405   string::const_iterator end = str.end();
406
407   bool r = qi::phrase_parse(iter, end, g, ascii::space, *this);
408   if (r && iter == end)
409     return true;
410
411   // Make sure no grants are kept after parsing failed!
412   grants.clear();
413
414   if (err)
415     *err << "osdcap parse failed, stopped at '" << std::string(iter, end)
416          << "' of '" << str << "'\n";
417
418   return false; 
419 }
420