Browse Source

[trac978] Loading of ACLs

Michal 'vorner' Vaner 14 years ago
parent
commit
941eceae0a
2 changed files with 114 additions and 22 deletions
  1. 95 16
      src/lib/acl/loader.h
  2. 19 6
      src/lib/acl/tests/loader_test.cc

+ 95 - 16
src/lib/acl/loader.h

@@ -97,14 +97,18 @@ public:
     /**
      * \brief Constructor.
      *
+     * \param default_action The default action for created ACLs.
      * \param actionLoader is the loader which will be used to convert actions
      *     from their JSON representation. The default value is suitable for
      *     the isc::acl::Action enum. If you did not specify the second
      *     template argument, you don't need to specify this loader.
      */
-    Loader(boost::function1<Action, data::ConstElementPtr> actionLoader =
-           &defaultActionLoader)
-    { }
+    Loader(const Action& defaultAction,
+           const boost::function1<Action, data::ConstElementPtr>
+               &actionLoader = &defaultActionLoader) :
+        default_action_(defaultAction),
+        action_loader_(actionLoader)
+    {}
     /**
      * \brief Creator of the checks.
      *
@@ -218,6 +222,82 @@ public:
                               "Check description is not a map",
                               description);
         }
+        // Call the internal part with extracted map
+        return (loadCheck(description, map));
+    }
+    /**
+     * \brief Load an ACL.
+     *
+     * This parses an ACL list, creates the checks and actions of each element
+     * and returns it. It may throw LoaderError if it isn't a list or the
+     * "action" key is missing in some element. Also, no exceptions from
+     * loadCheck (therefore from whatever creator is used) and from the
+     * actionLoader passed to constructor are not caught.
+     *
+     * \param description The JSON list of ACL.
+     */
+    boost::shared_ptr<Acl<Context, Action> > load(const data::ConstElementPtr&
+                                                  description)
+    {
+        // We first check it's a list, so we can use the list reference
+        // (the list may be huge)
+        if (description->getType() != data::Element::list) {
+            throw LoaderError(__FILE__, __LINE__, "ACL not a list",
+                              description);
+        }
+        // First create an empty ACL
+        const List &list(description->listValue());
+        boost::shared_ptr<Acl<Context, Action> > result(
+            new Acl<Context, Action>(default_action_));
+        // Run trough the list of elements
+        for (List::const_iterator i(list.begin()); i != list.end(); ++i) {
+            Map map;
+            try {
+                map = (*i)->mapValue();
+            }
+            catch (const data::TypeError&) {
+                throw LoaderError(__FILE__, __LINE__, "ACL element not a map",
+                                  *i);
+            }
+            // Create an action for the element
+            const Map::const_iterator action(map.find("action"));
+            if (action == map.end()) {
+                throw LoaderError(__FILE__, __LINE__,
+                                  "No action in ACL element", *i);
+            }
+            const Action acValue(action_loader_(action->second));
+            // Now create the check if there's one
+            if (map.size() >= 2) { // One is the action, another one the check
+                result->append(loadCheck(*i, map), acValue);
+            } else {
+                // In case there's no check, this matches every time. We
+                // simulate it by our own private "True" check.
+                result->append(boost::shared_ptr<Check<Context> >(new True()),
+                               acValue);
+            }
+        }
+        return (result);
+    }
+private:
+    // Some type aliases to save typing
+    typedef std::map<std::string, boost::shared_ptr<CheckCreator> > Creators;
+    typedef std::map<std::string, data::ConstElementPtr> Map;
+    typedef std::vector<data::ConstElementPtr> List;
+    // Private members
+    Creators creators_;
+    const Action default_action_;
+    const boost::function1<Action, data::ConstElementPtr> action_loader_;
+    /**
+     * \brief Internal version of loadCheck.
+     *
+     * This is the internal part, shared between load and loadCheck.
+     * \param description The bit of JSON (used in exceptions).
+     * \param map The extracted map describing the check. It does change
+     *     the map.
+     */
+    boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
+                                                 description, Map& map)
+    {
         // Remove the action keyword
         map.erase("action");
         // Now, do we have any definition? Or is it and abbreviation?
@@ -254,21 +334,20 @@ public:
         }
     }
     /**
-     * \brief Load an ACL.
+     * \brief Check that always matches.
      *
-     * This parses an ACL list, creates the checks and actions of each element
-     * and returns it. It may throw LoaderError if it isn't a list or the
-     * "action" key is missing in some element. Also, no exceptions from
-     * loadCheck (therefore from whatever creator is used) and from the
-     * actionLoader passed to constructor are not caught.
-     *
-     * \param description The JSON list of ACL.
+     * This one is used internally for ACL elements without condition. We may
+     * want to make this publicly accesible sometime maybe, but for now,
+     * there's no need.
      */
-    boost::shared_ptr<Acl<Context, Action> > load(const data::ConstElementPtr&
-                                                  description);
-private:
-    typedef std::map<std::string, boost::shared_ptr<CheckCreator> > Creators;
-    Creators creators_;
+    class True : public Check<Context> {
+    public:
+        virtual bool matches(const Context&) const { return (true); };
+        virtual unsigned cost() const { return (1); }
+        // We don't write "true" here, as this one was created using empty
+        // input
+        virtual std::string toText() const { return ""; }
+    };
 };
 
 }

+ 19 - 6
src/lib/acl/tests/loader_test.cc

@@ -171,10 +171,15 @@ public:
         bool accept(list[1]->boolValue());
         return (shared_ptr<ConstCheck>(new ConstCheck(accept, logpos)));
     }
+    // We take a list, so don't interpret it for us
+    virtual bool allowListAbbreviation() const { return (false); }
 };
 
 class LoaderTest : public ::testing::Test {
 public:
+    LoaderTest() :
+        loader_(REJECT)
+    {}
     Loader<Log*> loader_;
     Log log_;
     // Some convenience functions to set up
@@ -217,16 +222,23 @@ public:
     }
     // Insert the throw, throwcheck and logcheck checks into the loader
     void aclSetup() {
-        loader_.registerCreator(shared_ptr<ThrowCreator>(new ThrowCreator()));
-        loader_.registerCreator(shared_ptr<ThrowCheckCreator>(
-            new ThrowCheckCreator()));
-        loader_.registerCreator(shared_ptr<LogCreator>(new LogCreator()));
+        try {
+            loader_.registerCreator(shared_ptr<ThrowCreator>(new
+                                                             ThrowCreator()));
+            loader_.registerCreator(shared_ptr<ThrowCheckCreator>(
+                new ThrowCheckCreator()));
+            loader_.registerCreator(shared_ptr<LogCreator>(new LogCreator()));
+        }
+        // We ignore this exception here, because it happens when we try to
+        // insert the creators multiple times. This is harmless.
+        catch (const LoaderError&) {}
     }
     // Create an ACL, run it, check it's result and how many first
     // log items it marked
     //
     // Works with preset names throw and logcheck
     void aclRun(const string& JSON, Action expectedResult, size_t logged) {
+        SCOPED_TRACE("Running ACL for " + JSON);
         aclSetup();
         shared_ptr<Acl<Log*> > acl;
         EXPECT_NO_THROW(acl = loader_.load(el(JSON)));
@@ -235,6 +247,7 @@ public:
     }
     // Check it throws an error when creating the ACL
     void aclException(const string& JSON) {
+        SCOPED_TRACE("Trying to load bad " + JSON);
         aclSetup();
         EXPECT_THROW(loader_.load(el(JSON)), LoaderError);
     }
@@ -371,7 +384,7 @@ TEST_F(LoaderTest, MatchACL) {
 // We add another one check after it, to make sure it is really not run
 TEST_F(LoaderTest, NoCheckACL) {
     aclRun("["
-           "  {\"acton\": \"DROP\"},"
+           "  {\"action\": \"DROP\"},"
            "  {\"throwcheck\": 1, \"action\": \"ACCEPT\"}"
            "]", DROP, 0);
 }
@@ -401,7 +414,7 @@ TEST_F(LoaderTest, NoAction) {
 // Exceptions from check creation is propagated
 TEST_F(LoaderTest, ACLPropagate) {
     aclSetup();
-    EXPECT_THROW(loader_.load(el("[{\"action\": \"ACCEPT\", \"throw\": 1]")),
+    EXPECT_THROW(loader_.load(el("[{\"action\": \"ACCEPT\", \"throw\": 1}]")),
                  TestCreatorError);
 
 }