Added support for modularized authorizers.

Adds and integrates helper classes needed to support an `Authorizer`
module. Also adds a flag to the master, allowing the selection of an
`Authorizer` module.

Review: https://reviews.apache.org/r/36049


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/e6e4acb6
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/e6e4acb6
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/e6e4acb6

Branch: refs/heads/master
Commit: e6e4acb65024b0886e575bd8ab4cf47a3af84e4f
Parents: f9a014b
Author: Alexander Rojas <[email protected]>
Authored: Thu Aug 13 21:49:11 2015 +0200
Committer: Till Toenshoff <[email protected]>
Committed: Fri Aug 14 01:43:13 2015 +0200

----------------------------------------------------------------------
 docs/configuration.md                   | 17 ++++++++
 include/mesos/authorizer/authorizer.hpp |  3 ++
 include/mesos/module/authorizer.hpp     | 63 ++++++++++++++++++++++++++++
 src/Makefile.am                         |  2 +
 src/authorizer/authorizer.cpp           | 48 +++++++++++++++++++++
 src/local/local.cpp                     | 59 +++++++++++++++++++-------
 src/master/constants.cpp                |  1 +
 src/master/constants.hpp                |  3 ++
 src/master/flags.cpp                    | 18 ++++++++
 src/master/flags.hpp                    |  1 +
 src/master/main.cpp                     | 55 +++++++++++++++++-------
 src/module/manager.cpp                  |  1 +
 src/tests/cluster.hpp                   |  3 +-
 13 files changed, 243 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/docs/configuration.md
----------------------------------------------------------------------
diff --git a/docs/configuration.md b/docs/configuration.md
index 5143811..2b23d48 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -327,6 +327,23 @@ file:///path/to/file (where file contains one of the 
above)</code></pre>
   </tr>
   <tr>
     <td>
+      --authorizers=VALUE
+    </td>
+    <td>
+      Authorizer implementation to use when authorizating actions that
+      required it. Use the default <code>local</code>, or load an alternate
+      authorizer module using <code>--modules</code>.
+      <br/>
+      Note that if the flag <code>--authorizers</code> is provided with a
+      value different than the default <code>local</code>, the ACLs passed
+      through the <code>--acls</code> flag will be ignored.
+      <br/>
+      Currently there's no support for multiple authorizers.<br/>
+      (default: <code>local</code>)
+    </td>
+  </tr>
+  <tr>
+    <td>
       --cluster=VALUE
     </td>
     <td>

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/include/mesos/authorizer/authorizer.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/authorizer/authorizer.hpp 
b/include/mesos/authorizer/authorizer.hpp
index 6ee23c1..d667a52 100644
--- a/include/mesos/authorizer/authorizer.hpp
+++ b/include/mesos/authorizer/authorizer.hpp
@@ -20,6 +20,7 @@
 #define __MESOS_AUTHORIZER_AUTHORIZER_HPP__
 
 #include <ostream>
+#include <string>
 
 // ONLY USEFUL AFTER RUNNING PROTOC.
 #include <mesos/authorizer/authorizer.pb.h>
@@ -42,6 +43,8 @@ namespace mesos {
 class Authorizer
 {
 public:
+  static Try<Authorizer*> create(const std::string& name);
+
   virtual ~Authorizer() {}
 
   /**

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/include/mesos/module/authorizer.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/module/authorizer.hpp 
b/include/mesos/module/authorizer.hpp
new file mode 100644
index 0000000..7d8fc21
--- /dev/null
+++ b/include/mesos/module/authorizer.hpp
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESOS_MODULE_AUTHORIZER_HPP__
+#define __MESOS_MODULE_AUTHORIZER_HPP__
+
+#include <mesos/module.hpp>
+
+#include <mesos/authorizer/authorizer.hpp>
+
+namespace mesos {
+namespace modules {
+
+template <>
+inline const char* kind<mesos::Authorizer>()
+{
+  return "Authorizer";
+}
+
+
+template <>
+struct Module<mesos::Authorizer> : ModuleBase
+{
+  Module(
+      const char* _moduleApiVersion,
+      const char* _mesosVersion,
+      const char* _authorName,
+      const char* _authorEmail,
+      const char* _description,
+      bool (*_compatible)(),
+      mesos::Authorizer* (*_create)(const Parameters& parameters))
+    : ModuleBase(
+        _moduleApiVersion,
+        _mesosVersion,
+        mesos::modules::kind<mesos::Authorizer>(),
+        _authorName,
+        _authorEmail,
+        _description,
+        _compatible),
+      create(_create) {}
+
+  mesos::Authorizer* (*create)(const Parameters& parameters);
+};
+
+} // namespace modules {
+} // namespace mesos {
+
+#endif // __MESOS_MODULE_AUTHORIZER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index c481b6f..ae42d52 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -402,6 +402,7 @@ libmesos_no_3rdparty_la_SOURCES =                           
        \
        authentication/cram_md5/authenticatee.cpp                       \
        authentication/cram_md5/authenticator.cpp                       \
        authentication/cram_md5/auxprop.cpp                             \
+       authorizer/authorizer.cpp                                       \
        authorizer/local/authorizer.cpp                                 \
        common/attributes.cpp                                           \
        common/date_utils.cpp                                           \
@@ -549,6 +550,7 @@ module_HEADERS =                                            
        \
   $(top_srcdir)/include/mesos/module/anonymous.hpp                     \
   $(top_srcdir)/include/mesos/module/authenticatee.hpp                 \
   $(top_srcdir)/include/mesos/module/authenticator.hpp                 \
+  $(top_srcdir)/include/mesos/module/authorizer.hpp                    \
   $(top_srcdir)/include/mesos/module/hook.hpp                          \
   $(top_srcdir)/include/mesos/module/isolator.hpp                      \
   $(top_srcdir)/include/mesos/module/module.hpp                                
\

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/authorizer/authorizer.cpp
----------------------------------------------------------------------
diff --git a/src/authorizer/authorizer.cpp b/src/authorizer/authorizer.cpp
new file mode 100644
index 0000000..e1b7057
--- /dev/null
+++ b/src/authorizer/authorizer.cpp
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mesos/authorizer/authorizer.hpp>
+
+#include <mesos/module/authorizer.hpp>
+
+#include "authorizer/local/authorizer.hpp"
+
+#include "master/constants.hpp"
+
+#include "module/manager.hpp"
+
+using std::string;
+
+using mesos::internal::LocalAuthorizer;
+
+namespace mesos {
+
+Try<Authorizer*> Authorizer::create(const string& name)
+{
+  // Create an instance of the default authorizer. If other than the
+  // default authorizer is requested, search for it in loaded modules.
+  // NOTE: We do not need an extra not-null check, because both
+  // ModuleManager and built-in authorizer factory do that already.
+  if (name == mesos::internal::master::DEFAULT_AUTHORIZER) {
+    return LocalAuthorizer::create();
+  }
+
+  return modules::ModuleManager::create<Authorizer>(name);
+}
+
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/local/local.cpp
----------------------------------------------------------------------
diff --git a/src/local/local.cpp b/src/local/local.cpp
index 0390140..4d98bf2 100644
--- a/src/local/local.cpp
+++ b/src/local/local.cpp
@@ -28,6 +28,7 @@
 #include <mesos/master/allocator.hpp>
 
 #include <mesos/module/anonymous.hpp>
+#include <mesos/module/authorizer.hpp>
 
 #include <mesos/slave/resource_estimator.hpp>
 
@@ -43,8 +44,6 @@
 #include <stout/try.hpp>
 #include <stout/strings.hpp>
 
-#include "authorizer/local/authorizer.hpp"
-
 #include "common/protobuf_utils.hpp"
 
 #include "local.hpp"
@@ -215,23 +214,51 @@ PID<Master> launch(const Flags& flags, Allocator* 
_allocator)
     contender = new StandaloneMasterContender();
     detector = new StandaloneMasterDetector();
 
-    if (flags.acls.isSome()) {
-      Try<Authorizer*> local = LocalAuthorizer::create();
-
-      if (local.isError()) {
-        EXIT(EXIT_FAILURE)
-          << "Failed to instantiate the local authorizer: "
-          << local.error();
-      }
+    auto authorizerNames = strings::split(flags.authorizers, ",");
+    if (authorizerNames.empty()) {
+      EXIT(EXIT_FAILURE) << "No authorizer specified";
+    }
+    if (authorizerNames.size() > 1) {
+      EXIT(EXIT_FAILURE) << "Multiple authorizers not supported";
+    }
+    std::string authorizerName = authorizerNames[0];
 
-      authorizer = local.get();
+    // NOTE: The flag --authorizers overrides the flag --acls, i.e. if
+    // a non default authorizer is requested, it will be used and
+    // the contents of --acls will be ignored.
+    // TODO(arojas): Add support for multiple authorizers.
+    if (authorizerName != master::DEFAULT_AUTHORIZER ||
+        flags.acls.isSome()) {
+      Try<Authorizer*> create = Authorizer::create(authorizerName);
 
-      Try<Nothing> initialized = 
authorizer.get()->initialize(flags.acls.get());
+      if (create.isError()) {
+        EXIT(EXIT_FAILURE) << "Could not create '" << authorizerName
+                           << "' authorizer: " << create.error();
+      }
 
-      if (initialized.isError()) {
-        EXIT(EXIT_FAILURE)
-          << "Failed to initialize the local authorizer: "
-          << initialized.error() << " (see --acls flag)";
+      authorizer = create.get();
+
+      LOG(INFO) << "Using '" << authorizerName << "' authorizer";
+
+      if (authorizerName == master::DEFAULT_AUTHORIZER) {
+        Try<Nothing> initialize =
+          authorizer.get()->initialize(flags.acls.get());
+
+        if (initialize.isError()) {
+          // Failing to initialize the authorizer leads to undefined
+          // behavior, therefore we default to skip authorization
+          // altogether.
+          EXIT(EXIT_FAILURE) << "Failed to initialize '"
+                             << authorizerName << "' authorizer: "
+                             << initialize.error();
+
+          delete authorizer.get();
+          authorizer = None();
+        }
+      } else if (flags.acls.isSome()) {
+        LOG(WARNING) << "Ignoring contents of --acls flag, because '"
+                     << authorizerName << "' authorizer will be used instead"
+                     << " of the default.";
       }
     }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/constants.cpp
----------------------------------------------------------------------
diff --git a/src/master/constants.cpp b/src/master/constants.cpp
index 918dd70..2b66b27 100644
--- a/src/master/constants.cpp
+++ b/src/master/constants.cpp
@@ -47,6 +47,7 @@ const std::string MASTER_INFO_JSON_LABEL = "json.info";
 const Duration ZOOKEEPER_SESSION_TIMEOUT = Seconds(10);
 const std::string DEFAULT_AUTHENTICATOR = "crammd5";
 const std::string DEFAULT_ALLOCATOR = "HierarchicalDRF";
+const std::string DEFAULT_AUTHORIZER = "local";
 
 } // namespace master {
 } // namespace internal {

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/constants.hpp
----------------------------------------------------------------------
diff --git a/src/master/constants.hpp b/src/master/constants.hpp
index 7afa1ec..c3fe140 100644
--- a/src/master/constants.hpp
+++ b/src/master/constants.hpp
@@ -127,6 +127,9 @@ extern const std::string DEFAULT_AUTHENTICATOR;
 // Name of the default, HierarchicalDRF authenticator.
 extern const std::string DEFAULT_ALLOCATOR;
 
+// Name of the default, local authorizer.
+extern const std::string DEFAULT_AUTHORIZER;
+
 } // namespace master {
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/flags.cpp
----------------------------------------------------------------------
diff --git a/src/master/flags.cpp b/src/master/flags.cpp
index 60ac64d..230c1dc 100644
--- a/src/master/flags.cpp
+++ b/src/master/flags.cpp
@@ -227,6 +227,9 @@ mesos::internal::master::Flags::Flags()
       "for authorization. Path could be of the form 'file:///path/to/file'\n"
       "or '/path/to/file'.\n"
       "\n"
+      "Note that if the flag --authorizers is provided with a value 
different\n"
+      "than '" + DEFAULT_AUTHORIZER + "', the ACLs contents will be ignored.\n"
+      "\n"
       "See the ACLs protobuf in mesos.proto for the expected format.\n"
       "\n"
       "Example:\n"
@@ -407,4 +410,19 @@ mesos::internal::master::Flags::Flags()
         }
         return None();
       });
+
+
+  add(&Flags::authorizers,
+      "authorizers",
+      "Authorizer implementation to use when authorizating actions that\n"
+      "required it.\n"
+      "Use the default '" + DEFAULT_AUTHORIZER + "', or\n"
+      "load an alternate authorizer module using --modules.\n"
+      "\n"
+      "Note that if the flag --authorizers is provided with a value 
different\n"
+      "than the default '" + DEFAULT_AUTHORIZER + "', the ACLs passed\n"
+      "through the --acls flag will be ignored.\n"
+      "\n"
+      "Currently there's no support for multiple authorizers.",
+      DEFAULT_AUTHORIZER);
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/flags.hpp
----------------------------------------------------------------------
diff --git a/src/master/flags.hpp b/src/master/flags.hpp
index 25e4631..e4b1df3 100644
--- a/src/master/flags.hpp
+++ b/src/master/flags.hpp
@@ -78,6 +78,7 @@ public:
   Option<std::string> hooks;
   Duration slave_ping_timeout;
   size_t max_slave_ping_timeouts;
+  std::string authorizers;
 
 #ifdef WITH_NETWORK_ISOLATOR
   Option<size_t> max_executors_per_slave;

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/main.cpp
----------------------------------------------------------------------
diff --git a/src/master/main.cpp b/src/master/main.cpp
index 3b4a2c1..e024a2e 100644
--- a/src/master/main.cpp
+++ b/src/master/main.cpp
@@ -31,6 +31,7 @@
 #include <mesos/master/allocator.hpp>
 
 #include <mesos/module/anonymous.hpp>
+#include <mesos/module/authorizer.hpp>
 
 #include <process/limiter.hpp>
 #include <process/owned.hpp>
@@ -48,8 +49,6 @@
 #include <stout/strings.hpp>
 #include <stout/try.hpp>
 
-#include "authorizer/local/authorizer.hpp"
-
 #include "common/build.hpp"
 #include "common/protobuf_utils.hpp"
 
@@ -367,23 +366,51 @@ int main(int argc, char** argv)
   detector = detector_.get();
 
   Option<Authorizer*> authorizer = None();
-  if (flags.acls.isSome()) {
-    Try<Authorizer*> local = LocalAuthorizer::create();
 
-    if (local.isError()) {
-      EXIT(EXIT_FAILURE)
-        << "Failed to instantiate the local authorizer: "
-        << local.error();
+  auto authorizerNames = strings::split(flags.authorizers, ",");
+  if (authorizerNames.empty()) {
+    EXIT(EXIT_FAILURE) << "No authorizer specified";
+  }
+  if (authorizerNames.size() > 1) {
+    EXIT(EXIT_FAILURE) << "Multiple authorizers not supported";
+  }
+  std::string authorizerName = authorizerNames[0];
+
+  // NOTE: The flag --authorizers overrides the flag --acls, i.e. if
+  // a non default authorizer is requested, it will be used and
+  // the contents of --acls will be ignored.
+  // TODO(arojas): Add support for multiple authorizers.
+  if (authorizerName != master::DEFAULT_AUTHORIZER ||
+      flags.acls.isSome()) {
+    Try<Authorizer*> create = Authorizer::create(authorizerName);
+
+    if (create.isError()) {
+      EXIT(EXIT_FAILURE) << "Could not create '" << authorizerName
+                         << "' authorizer: " << create.error();
     }
 
-    authorizer = local.get();
+    authorizer = create.get();
 
-    Try<Nothing> initialized = authorizer.get()->initialize(flags.acls.get());
+    LOG(INFO) << "Using '" << authorizerName << "' authorizer";
 
-    if (initialized.isError()) {
-      EXIT(EXIT_FAILURE)
-        << "Failed to initialize the local authorizer: "
-        << initialized.error() << " (see --acls flag)";
+    if (authorizerName == master::DEFAULT_AUTHORIZER) {
+      Try<Nothing> initialize = authorizer.get()->initialize(flags.acls.get());
+
+      if (initialize.isError()) {
+        // Failing to initialize the authorizer leads to undefined
+        // behavior, therefore we default to skip authorization
+        // altogether.
+        LOG(WARNING) << "Authorization disabled: Failed to initialize '"
+                     << authorizerName << "' authorizer: "
+                     << initialize.error();
+
+        delete authorizer.get();
+        authorizer = None();
+      }
+    } else if (flags.acls.isSome()) {
+      LOG(WARNING) << "Ignoring contents of --acls flag, because '"
+                   << authorizerName << "' authorizer will be used instead "
+                   << " of the default.";
     }
   }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/module/manager.cpp
----------------------------------------------------------------------
diff --git a/src/module/manager.cpp b/src/module/manager.cpp
index 909ca56..862b71f 100644
--- a/src/module/manager.cpp
+++ b/src/module/manager.cpp
@@ -65,6 +65,7 @@ void ModuleManager::initialize()
   kindToVersion["Anonymous"] = MESOS_VERSION;
   kindToVersion["Authenticatee"] = MESOS_VERSION;
   kindToVersion["Authenticator"] = MESOS_VERSION;
+  kindToVersion["Authorizer"] = MESOS_VERSION;
   kindToVersion["Hook"] = MESOS_VERSION;
   kindToVersion["Isolator"] = MESOS_VERSION;
   kindToVersion["QoSController"] = MESOS_VERSION;

http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/tests/cluster.hpp
----------------------------------------------------------------------
diff --git a/src/tests/cluster.hpp b/src/tests/cluster.hpp
index b59b750..114583d 100644
--- a/src/tests/cluster.hpp
+++ b/src/tests/cluster.hpp
@@ -63,6 +63,7 @@
 
 #include "log/tool/initialize.hpp"
 
+#include "master/constants.hpp"
 #include "master/contender.hpp"
 #include "master/detector.hpp"
 #include "master/flags.hpp"
@@ -351,7 +352,7 @@ inline Try<process::PID<master::Master>> 
Cluster::Masters::start(
   if (authorizer.isSome()) {
     CHECK_NOTNULL(authorizer.get());
   } else if (flags.acls.isSome()) {
-    Try<Authorizer*> local = LocalAuthorizer::create();
+    Try<Authorizer*> local = Authorizer::create(master::DEFAULT_AUTHORIZER);
 
     if (local.isError()) {
       EXIT(EXIT_FAILURE)

Reply via email to