]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
objclass: implement 'osd class default list'
authorNoah Watkins <noahwatkins@gmail.com>
Mon, 27 Jun 2016 01:10:22 +0000 (18:10 -0700)
committerNoah Watkins <noahwatkins@gmail.com>
Wed, 13 Jul 2016 15:03:26 +0000 (08:03 -0700)
Signed-off-by: Noah Watkins <noahwatkins@gmail.com>
src/common/config_opts.h
src/osd/ClassHandler.cc
src/osd/ClassHandler.h
src/osd/OSD.cc
src/osd/OSDCap.cc
src/osd/OSDCap.h
src/osd/OpRequest.cc
src/osd/OpRequest.h
src/osd/PG.cc
src/test/osd/osdcap.cc
src/vstart.sh

index 21a9d378a1eb31c77cdb5db436c7939dbfdc318f..ec870a4c2dace153fe45be80977125820d99beec 100644 (file)
@@ -777,6 +777,8 @@ OPTION(osd_class_dir, OPT_STR, CEPH_LIBDIR "/rados-classes") // where rados plug
 OPTION(osd_open_classes_on_start, OPT_BOOL, true)
 OPTION(osd_class_load_list, OPT_STR, "cephfs hello journal lock log numops "
     "rbd refcount replica_log rgw statelog timeindex user version") // list of object classes allowed to be loaded (allow all: *)
+OPTION(osd_class_default_list, OPT_STR, "cephfs hello journal lock log numops "
+    "rbd refcount replica_log rgw statelog timeindex user version") // list of object classes with default execute perm (allow all: *)
 OPTION(osd_check_for_log_corruption, OPT_BOOL, false)
 OPTION(osd_use_stale_snap, OPT_BOOL, false)
 OPTION(osd_rollback_to_cluster_snap, OPT_STR, "")
index d116f75ad897a59f81b4f3faf58edce01d6bbc7e..46ba48d19e6cb1766521be8cb8b6efc8650fca4c 100644 (file)
@@ -125,6 +125,7 @@ ClassHandler::ClassData *ClassHandler::_get_class(const string& cname,
     dout(10) << "_get_class adding new class name " << cname << " " << cls << dendl;
     cls->name = cname;
     cls->handler = this;
+    cls->whitelisted = in_class_list(cname, cct->_conf->osd_class_default_list);
   }
   return cls;
 }
index 6bdf6705052d80e36401e7e32ce9f802c13def17..f0e09422fcecb996d83415ccb69a6be6521ae030 100644 (file)
@@ -57,6 +57,8 @@ public:
     ClassHandler *handler;
     void *handle;
 
+    bool whitelisted;
+
     map<string, ClassMethod> methods_map;
     map<string, ClassFilter> filters_map;
 
index f5ec2c505b5b4f0af31ef3afe9fa4ff6bd2a91fb..b06953c12964f66ccb0bad494f22ba8b5ccc4620 100644 (file)
@@ -9246,6 +9246,7 @@ int OSD::init_op_flags(OpRequestRef& op)
          op->set_class_write();
         if (is_promote)
           op->set_promote();
+        op->add_class(cname, is_read, is_write, cls->whitelisted);
        break;
       }
 
index b9944e57e54cec4609bc22bd0db5d235a9053585..c8cfbfab1f366a45a0dba0204d78bef773360937 100644 (file)
@@ -130,20 +130,46 @@ void OSDCap::set_allow_all()
 }
 
 bool OSDCap::is_capable(const string& pool_name, const string& ns, int64_t pool_auid,
-                       const string& object, bool op_may_read,
-                       bool op_may_write, bool op_may_class_read,
-                       bool op_may_class_write) const
+                       const string& object, bool op_may_read, bool op_may_write,
+                       const std::vector<OpRequest::ClassInfo>& classes) const
 {
+  const size_t num_classes = classes.size();
+  std::vector<bool> class_allowed(num_classes, false);
   osd_rwxa_t allow = 0;
   for (vector<OSDCapGrant>::const_iterator p = grants.begin();
        p != grants.end(); ++p) {
     if (p->match.is_match(pool_name, ns, pool_auid, object)) {
       allow = allow | p->spec.allow;
       if ((op_may_read && !(allow & OSD_CAP_R)) ||
-         (op_may_write && !(allow & OSD_CAP_W)) ||
-         (op_may_class_read && !(allow & OSD_CAP_CLS_R)) ||
-         (op_may_class_write && !(allow & OSD_CAP_CLS_W)))
-       continue;
+          (op_may_write && !(allow & OSD_CAP_W)))
+        continue;
+      if (num_classes) {
+        // check 'allow *'
+        if (p->spec.allow_all())
+          return true;
+        // compare this grant to each class in the operation
+        for (size_t i = 0; i < num_classes; i++) {
+          // check 'allow class foo'
+          if (classes[i].name == p->spec.class_name &&
+              !p->spec.class_name.empty()) {
+            class_allowed[i] = true;
+            continue;
+          }
+          // check 'allow x | class-{rw}': must be on whitelist
+          if (!classes[i].whitelisted)
+            continue;
+          if ((classes[i].read && !(allow & OSD_CAP_CLS_R)) ||
+              (classes[i].write && !(allow & OSD_CAP_CLS_W))) {
+            continue;
+          }
+          class_allowed[i] = true;
+        }
+        if (std::all_of(class_allowed.cbegin(), class_allowed.cend(),
+              [](bool v) { return v; }))
+          return true;
+        else
+          continue;
+      }
       return true;
     }
   }
index 2a1eb0d4916062e27560de8ef73570e794007c12..3a1a722aaa7144b4c7567cdada9ec0b2454971dd 100644 (file)
@@ -31,6 +31,7 @@
 using std::ostream;
 
 #include "include/types.h"
+#include "OpRequest.h"
 
 static const __u8 OSD_CAP_R     = (1 << 1);      // read
 static const __u8 OSD_CAP_W     = (1 << 2);      // write
@@ -140,15 +141,12 @@ struct OSDCap {
    * @param object name of the object we are accessing
    * @param op_may_read whether the operation may need to read
    * @param op_may_write whether the operation may need to write
-   * @param op_may_class_read whether the operation needs to call a
-   *                          read class method
-   * @param op_may_class_write whether the operation needs to call a
-   *                          write class method
+   * @param classes (class-name, rd, wr, whitelisted-flag) tuples
    * @return true if the operation is allowed, false otherwise
    */
   bool is_capable(const string& pool_name, const string& ns, int64_t pool_auid,
                  const string& object, bool op_may_read, bool op_may_write,
-                 bool op_may_class_read, bool op_may_class_write) const;
+                 const std::vector<OpRequest::ClassInfo>& classes) const;
 };
 
 static inline ostream& operator<<(ostream& out, const OSDCap& cap) 
index 9bad447b69fba3239384ac772fc1b00f063318cf..746f8efc9b31a250bbf1c58b6736e6f813ccd20b 100644 (file)
@@ -89,8 +89,12 @@ bool OpRequest::check_rmw(int flag) {
   assert(rmw_flags != 0);
   return rmw_flags & flag;
 }
-bool OpRequest::may_read() { return need_read_cap() || need_class_read_cap(); }
-bool OpRequest::may_write() { return need_write_cap() || need_class_write_cap(); }
+bool OpRequest::may_read() {
+  return need_read_cap() || check_rmw(CEPH_OSD_RMW_FLAG_CLASS_READ);
+}
+bool OpRequest::may_write() {
+  return need_write_cap() || check_rmw(CEPH_OSD_RMW_FLAG_CLASS_WRITE);
+}
 bool OpRequest::may_cache() { return check_rmw(CEPH_OSD_RMW_FLAG_CACHE); }
 bool OpRequest::includes_pg_op() { return check_rmw(CEPH_OSD_RMW_FLAG_PGOP); }
 bool OpRequest::need_read_cap() {
@@ -99,12 +103,6 @@ bool OpRequest::need_read_cap() {
 bool OpRequest::need_write_cap() {
   return check_rmw(CEPH_OSD_RMW_FLAG_WRITE);
 }
-bool OpRequest::need_class_read_cap() {
-  return check_rmw(CEPH_OSD_RMW_FLAG_CLASS_READ);
-}
-bool OpRequest::need_class_write_cap() {
-  return check_rmw(CEPH_OSD_RMW_FLAG_CLASS_WRITE);
-}
 bool OpRequest::need_promote() {
   return check_rmw(CEPH_OSD_RMW_FLAG_FORCE_PROMOTE);
 }
@@ -147,3 +145,10 @@ void OpRequest::mark_flag_point(uint8_t flag, const string& s) {
             reqid.name._num, reqid.tid, reqid.inc, rmw_flags,
             flag, s.c_str(), old_flags, hit_flag_points);
 }
+
+ostream& operator<<(ostream& out, const OpRequest::ClassInfo& i)
+{
+  out << "class " << i.name << " rd " << i.read
+    << " wr " << i.write << " wl " << i.whitelisted;
+  return out;
+}
index c8649b8500a3e617ef0e5e77aa5385e14f613f20..7982e1a44b461234d256d86ec191c3a61a9147d0 100644 (file)
@@ -63,8 +63,6 @@ struct OpRequest : public TrackedOp {
   bool includes_pg_op();
   bool need_read_cap();
   bool need_write_cap();
-  bool need_class_read_cap();
-  bool need_class_write_cap();
   bool need_promote();
   bool need_skip_handle_cache();
   bool need_skip_promote();
@@ -78,6 +76,24 @@ struct OpRequest : public TrackedOp {
   void set_skip_handle_cache();
   void set_skip_promote();
 
+  struct ClassInfo {
+    ClassInfo(const std::string& name, bool read, bool write,
+        bool whitelisted) :
+      name(name), read(read), write(write), whitelisted(whitelisted)
+    {}
+    const std::string name;
+    const bool read, write, whitelisted;
+  };
+
+  void add_class(const std::string& name, bool read, bool write,
+      bool whitelisted) {
+    classes_.emplace_back(name, read, write, whitelisted);
+  }
+
+  std::vector<ClassInfo> classes() const {
+    return classes_;
+  }
+
   void _dump(utime_t now, Formatter *f) const;
 
   bool has_feature(uint64_t f) const {
@@ -97,6 +113,8 @@ private:
   static const uint8_t flag_sub_op_sent = 1 << 4;
   static const uint8_t flag_commit_sent = 1 << 5;
 
+  std::vector<ClassInfo> classes_;
+
   OpRequest(Message *req, OpTracker *tracker);
 
 protected:
@@ -176,4 +194,6 @@ private:
 
 typedef OpRequest::Ref OpRequestRef;
 
+ostream& operator<<(ostream& out, const OpRequest::ClassInfo& i);
+
 #endif /* OPREQUEST_H_ */
index 9f4136f56667f00b7a40bd04a218153ef77b6a3a..228cb1b397bbea31bd037dea83a383a4c236b23a 100644 (file)
@@ -1869,16 +1869,14 @@ bool PG::op_has_sufficient_caps(OpRequestRef& op)
                              pool.auid, key,
                             op->need_read_cap(),
                             op->need_write_cap(),
-                            op->need_class_read_cap(),
-                            op->need_class_write_cap());
+                            op->classes());
 
   dout(20) << "op_has_sufficient_caps pool=" << pool.id << " (" << pool.name
                   << " " << req->get_object_locator().nspace
           << ") owner=" << pool.auid
           << " need_read_cap=" << op->need_read_cap()
           << " need_write_cap=" << op->need_write_cap()
-          << " need_class_read_cap=" << op->need_class_read_cap()
-          << " need_class_write_cap=" << op->need_class_write_cap()
+          << " classes=" << op->classes()
           << " -> " << (cap ? "yes" : "NO")
           << dendl;
   return cap;
index a9aefd729e37154f266c805547176e6e7ad21dce..95dc40ba556936047eaf19c95d6bf088d0a706d5 100644 (file)
@@ -150,10 +150,16 @@ TEST(OSDCap, AllowAll) {
 
   ASSERT_TRUE(cap.parse("allow *", NULL));
   ASSERT_TRUE(cap.allow_all());
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "asdf", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "anamespace", 0, "asdf", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "asdf", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "anamespace", 0, "asdf", true, true, true, true));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "asdf", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "anamespace", 0, "asdf", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "asdf", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "anamespace", 0, "asdf", true, true, {{"cls", true, true, true}}));
+
+  // 'allow *' overrides whitelist
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "asdf", true, true, {{"cls", true, true, false}}));
+  ASSERT_TRUE(cap.is_capable("foo", "anamespace", 0, "asdf", true, true, {{"cls", true, true, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "asdf", true, true, {{"cls", true, true, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "anamespace", 0, "asdf", true, true, {{"cls", true, true, false}}));
 }
 
 TEST(OSDCap, AllowPool) {
@@ -161,10 +167,14 @@ TEST(OSDCap, AllowPool) {
   bool r = cap.parse("allow rwx pool foo", NULL);
   ASSERT_TRUE(r);
 
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "", true, true, true, true));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "", true, true, {{"cls", true, true, true}}));
+  // true->false for classes not on whitelist
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "", true, true, {{"cls", true, true, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "", true, true, {{"cls", true, true, true}}));
 }
 
 TEST(OSDCap, AllowPools) {
@@ -172,14 +182,20 @@ TEST(OSDCap, AllowPools) {
   bool r = cap.parse("allow rwx pool foo, allow r pool bar", NULL);
   ASSERT_TRUE(r);
 
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("baz", "ns", 0, "", true, false, false, false));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "", true, true, {{"cls", true, true, true}}));
+  // true-false for classes not on whitelist
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "", true, true, {{"cls", true, true, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "", true, false, {}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "", true, true, {{"cls", true, true, true}}));
+
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("baz", "ns", 0, "", true, false, {}));
 }
 
 TEST(OSDCap, AllowPools2) {
@@ -187,9 +203,12 @@ TEST(OSDCap, AllowPools2) {
   bool r = cap.parse("allow r, allow rwx pool foo", NULL);
   ASSERT_TRUE(r);
 
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "", true, false, false, false));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "", true, true, {{"cls", true, true, true}}));
+  // true-false for classes not on whitelist
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "", true, true, {{"cls", true, true, true}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "", true, false, {}));
 }
 
 TEST(OSDCap, ObjectPrefix) {
@@ -197,13 +216,17 @@ TEST(OSDCap, ObjectPrefix) {
   bool r = cap.parse("allow rwx object_prefix foo", NULL);
   ASSERT_TRUE(r);
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "food", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo_bar", true, true, true, true));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "_foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, " foo ", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "fo", true, true, true, true));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "food", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo_bar", true, true, {{"cls", true, true, true}}));
+  // true-false for classes not on whitelist
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "food", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo_bar", true, true, {{"cls", true, true, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "_foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, " foo ", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "fo", true, true, {{"cls", true, true, true}}));
 }
 
 TEST(OSDCap, ObjectPoolAndPrefix) {
@@ -211,324 +234,412 @@ TEST(OSDCap, ObjectPoolAndPrefix) {
   bool r = cap.parse("allow rwx pool bar object_prefix foo", NULL);
   ASSERT_TRUE(r);
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "food", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo_bar", true, true, true, true));
-
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "food", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "fo", true, true, true, true));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "food", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo_bar", true, true, {{"cls", true, true, true}}));
+  // true-false for classes not on whitelist
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "food", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo_bar", true, true, {{"cls", true, true, false}}));
+
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "food", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "fo", true, true, {{"cls", true, true, true}}));
 }
 
 TEST(OSDCap, BasicR) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow r", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
 
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
 }
 
 TEST(OSDCap, BasicW) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow w", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
 
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
 }
 
 TEST(OSDCap, BasicX) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow x", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, true));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  // true->false when class not on whitelist
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
 }
 
 TEST(OSDCap, BasicRW) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow rw", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
 
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
 }
 
 TEST(OSDCap, BasicRX) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow rx", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, true, true));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, true, true}}));
+  // true->false for class not on whitelist
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, true, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
 }
 
 TEST(OSDCap, BasicWX) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow wx", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, true, true));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  // true->false for class not on whitelist
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
 }
 
 TEST(OSDCap, BasicRWX) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow rwx", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, false, true}}));
+  // true->false for class not on whitelist
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, false, false}}));
 }
 
 TEST(OSDCap, BasicRWClassRClassW) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow rw class-read class-write", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, false, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, false, false}}));
 }
 
 TEST(OSDCap, ClassR) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow class-read", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, false}}));
 
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, false, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
 }
 
 TEST(OSDCap, ClassW) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow class-write", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, false, true));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, false}}));
 
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
 }
 
 TEST(OSDCap, ClassRW) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow class-read class-write", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, true));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
 }
 
 TEST(OSDCap, BasicRClassR) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow r class-read", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
-
-  ASSERT_TRUE(cap.is_capable("bar", "any", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "any", 0, "foo", true, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "any", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", true, true, false, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "any", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "any", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "any", 0, "foo", true, false, {}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", true, false, {{"cls", true, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "any", 0, "foo", true, true, {}));
 }
 
 TEST(OSDCap, PoolClassR) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow pool bar r class-read, allow pool foo rwx", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
-
-  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "foo", true, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, true, false, false));
-
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, true, false));
-
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, true, false));
-
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, false, false));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, false, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, false, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, true, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "ns", 0, "foo", true, false, {}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, false, {{"cls", true, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, true, {}));
+
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, false, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, false, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, false, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", false, false, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", false, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, {}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, {{"cls", true, false, true}}));
 }
 
 TEST(OSDCap, PoolClassRNS) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow pool bar namespace='' r class-read, allow pool foo namespace=ns rwx", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, true, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", false, false, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, false, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", true, true, false, false));
-
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, false, false, false));
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, false, true, true));
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, false, false, true));
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, false, true));
-  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, true, false));
-
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, true, false));
-
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, false, false));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, true, true, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, false, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, false, true));
-  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, true, false));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", true, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "ns", 0, "foo", true, false, {}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("bar", "other", 0, "foo", true, true, {}));
+
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, false, {}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, false, true}}));
+
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, false, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", false, false, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", false, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "ns", 0, "foo", true, true, {{"cls", true, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, {}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("baz", "", 0, "foo", true, true, {{"cls", true, false, true}}));
 }
 
 TEST(OSDCap, NSClassR) {
   OSDCap cap;
   ASSERT_TRUE(cap.parse("allow namespace '' rw class-read class-write, allow namespace test r", NULL));
 
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, true, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, false, true));
-  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, true, false));
-
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, true, true, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, false, false));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, false, true));
-  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, true, false));
-
-  ASSERT_TRUE(cap.is_capable("bar", "test", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", true, false, true, false));
-  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", true, true, false, false));
-
-  ASSERT_TRUE(cap.is_capable("foo", "test", 0, "foo", true, false, false, false));
-
-  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", true, false, true, false));
-  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", true, true, true, true));
-  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", false, true, false, true));
-  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", true, true, false, false));
-
-  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", true, false, false, false));
-  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", false, true, false, false));
-  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", false, false, true, false));
-  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", false, false, false, true));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, false, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", true, true, {{"cls", true, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, false, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", false, true, {{"cls", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, false, {}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, false, true}}));
+  // true->false when class not whitelisted
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, false, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", false, true, {{"cls", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, false, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("foo", "", 0, "foo", true, true, {{"cls", true, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "test", 0, "foo", true, false, {}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "test", 0, "foo", true, true, {}));
+
+  ASSERT_TRUE(cap.is_capable("foo", "test", 0, "foo", true, false, {}));
+
+  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", true, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", true, true, {{"cls", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", false, true, {{"cls", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "test", 0, "foo", true, true, {}));
+
+  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", true, false, {}));
+  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", false, true, {}));
+  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", false, false, {{"cls", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("foo", "bad", 0, "foo", false, false, {{"cls", false, true, true}}));
 }
 
 TEST(OSDCap, OutputParsed)
@@ -588,3 +699,310 @@ TEST(OSDCap, OutputParsed)
     ASSERT_EQ(test_values[i].output, stringify(cap));
   }
 }
+
+TEST(OSDCap, AllowClass) {
+  OSDCap cap;
+  ASSERT_TRUE(cap.parse("allow class foo", NULL));
+
+  // can call any method on class foo regardless of whitelist status
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}}));
+
+  // does not permit invoking class bar
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, true, false}}));
+}
+
+TEST(OSDCap, AllowClass2) {
+  OSDCap cap;
+  ASSERT_TRUE(cap.parse("allow class foo, allow class bar", NULL));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, false, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", false, true, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, true, false}}));
+}
+
+TEST(OSDCap, AllowClassRWX) {
+  OSDCap cap;
+  ASSERT_TRUE(cap.parse("allow rwx, allow class foo", NULL));
+
+  // can call any method on class foo regardless of whitelist status
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}}));
+
+  // does not permit invoking class bar
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, true, false}}));
+
+  // allows class bar if it is whitelisted
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"bar", true, true, true}}));
+}
+
+TEST(OSDCap, AllowClassMulti) {
+  OSDCap cap;
+  ASSERT_TRUE(cap.parse("allow class foo", NULL));
+
+  // can call any method on foo, but not bar, so the entire op is rejected
+  // bar with whitelist is rejected because it still needs rwx/class-read,write
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, false, false}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, false, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, true, true}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, false, false}}));
+
+  // these are OK because 'bar' is on the whitelist BUT the calls don't read or write
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, false, true}}));
+
+  // can call any method on foo or bar regardless of whitelist status
+  OSDCap cap2;
+  ASSERT_TRUE(cap2.parse("allow class foo, allow class bar", NULL));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, false, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, true, false}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, false, true}}));
+  ASSERT_TRUE(cap2.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, false, false}}));
+}
+
+TEST(OSDCap, AllowClassMultiRWX) {
+  OSDCap cap;
+  ASSERT_TRUE(cap.parse("allow rwx, allow class foo", NULL));
+
+  // can call anything on foo, but only whitelisted methods on bar
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, false, true}}));
+
+  // fails because bar not whitelisted
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, false, true}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, true, false}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, false, true}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, false, true}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", true, false, false}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, false, true}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, false, true}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, true, false}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, false, true}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, true}, {"bar", false, false, false}}));
+
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, false, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, true, true}}));
+  ASSERT_TRUE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, false, true}}));
+
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", true, false, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, true, false}}));
+  ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, false, false}}));
+}
index 5f06abde559f667929f93c5e4a93e9bac698b79e..d217e626dda0365ab263b31dbdf27593d1b6b6cd 100755 (executable)
@@ -525,6 +525,7 @@ $DAEMONOPTS
         osd class tmp = out
         osd class dir = $OBJCLASS_PATH
         osd class load list = *
+        osd class default list = *
         osd scrub load threshold = 2000.0
         osd debug op order = true
         filestore wbthrottle xfs ios start flusher = 10