storm-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From srihar...@apache.org
Subject [05/10] storm git commit: STORM-446: Adding doAsUser support to REST APIs.
Date Thu, 12 Mar 2015 17:06:51 GMT
STORM-446: Adding doAsUser support to REST APIs.


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

Branch: refs/heads/master
Commit: e353fe006fc07573ddbfa2e0e9d57fea3a3fd1a4
Parents: 807e5cf
Author: Parth Brahmbhatt <brahmbhatt.parth@gmail.com>
Authored: Sat Feb 28 19:56:14 2015 -0800
Committer: Parth Brahmbhatt <brahmbhatt.parth@gmail.com>
Committed: Sat Feb 28 19:56:14 2015 -0800

----------------------------------------------------------------------
 STORM-UI-REST-API.md                            |  5 ++
 storm-core/src/clj/backtype/storm/thrift.clj    | 17 +++++--
 storm-core/src/clj/backtype/storm/ui/core.clj   | 50 +++++++++++++-------
 .../auth/DefaultHttpCredentialsPlugin.java      | 24 ++++++----
 4 files changed, 68 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/storm/blob/e353fe00/STORM-UI-REST-API.md
----------------------------------------------------------------------
diff --git a/STORM-UI-REST-API.md b/STORM-UI-REST-API.md
index e89595f..9a0043a 100644
--- a/STORM-UI-REST-API.md
+++ b/STORM-UI-REST-API.md
@@ -32,6 +32,11 @@ You can use a tool such as `curl` to talk to the REST API:
     # Note: We assume ui.port is configured to the default value of 8080.
     $ curl http://<ui-host>:8080/api/v1/cluster/configuration
 
+##Impersonating a user in secure environment
+In a secure environment an authenticated user can impersonate another user. To impersonate
a user the caller must pass
+`doAsUser` param or header with value set to the user that the request needs to be performed
as. Please see SECURITY.MD
+to learn more about how to setup impersonation ACLs and authorization. The rest API uses
the same configs and acls that
+are used by nimbus.
 
 ## GET Operations
 

http://git-wip-us.apache.org/repos/asf/storm/blob/e353fe00/storm-core/src/clj/backtype/storm/thrift.clj
----------------------------------------------------------------------
diff --git a/storm-core/src/clj/backtype/storm/thrift.clj b/storm-core/src/clj/backtype/storm/thrift.clj
index ce0a5ff..7689c87 100644
--- a/storm-core/src/clj/backtype/storm/thrift.clj
+++ b/storm-core/src/clj/backtype/storm/thrift.clj
@@ -66,13 +66,15 @@
     (if-not (.is_set_parallelism_hint component-common) 1 phint)))
 
 (defn nimbus-client-and-conn
-  [host port]
-  (log-message "Connecting to Nimbus at " host ":" port)
+  ([host port]
+    (nimbus-client-and-conn host port nil))
+  ([host port as-user]
+  (log-message "Connecting to Nimbus at " host ":" port " as user: " as-user)
   (let [conf (read-storm-config)
-        nimbusClient (NimbusClient. conf host port nil)
+        nimbusClient (NimbusClient. conf host port nil as-user)
         client (.getClient nimbusClient)
         transport (.transport nimbusClient)]
-        [client transport] ))
+        [client transport] )))
 
 (defmacro with-nimbus-connection
   [[client-sym host port] & body]
@@ -81,6 +83,13 @@
       ~@body
     (finally (.close conn#)))))
 
+(defmacro with-nimbus-connection-as-user
+  [[client-sym host port as-user] & body]
+  `(let [[^Nimbus$Client ~client-sym ^TTransport conn#] (nimbus-client-and-conn ~host ~port
~as-user)]
+     (try
+       ~@body
+       (finally (.close conn#)))))
+
 (defmacro with-configured-nimbus-connection
   [client-sym & body]
   `(let [conf# (read-storm-config)

http://git-wip-us.apache.org/repos/asf/storm/blob/e353fe00/storm-core/src/clj/backtype/storm/ui/core.clj
----------------------------------------------------------------------
diff --git a/storm-core/src/clj/backtype/storm/ui/core.clj b/storm-core/src/clj/backtype/storm/ui/core.clj
index f656b12..c64f35d 100644
--- a/storm-core/src/clj/backtype/storm/ui/core.clj
+++ b/storm-core/src/clj/backtype/storm/ui/core.clj
@@ -45,28 +45,46 @@
 
 (def ^:dynamic *STORM-CONF* (read-storm-config))
 (def ^:dynamic *UI-ACL-HANDLER* (mk-authorization-handler (*STORM-CONF* NIMBUS-AUTHORIZER)
*STORM-CONF*))
+(def ^:dynamic *UI-IMPERSONATION-HANDLER* (mk-authorization-handler (*STORM-CONF* NIMBUS-IMPERSONATION-AUTHORIZER)
*STORM-CONF*))
 
 (def http-creds-handler (AuthUtils/GetUiHttpCredentialsPlugin *STORM-CONF*))
 
 (defmacro with-nimbus
   [nimbus-sym & body]
-  `(thrift/with-nimbus-connection
-     [~nimbus-sym (*STORM-CONF* NIMBUS-HOST) (*STORM-CONF* NIMBUS-THRIFT-PORT)]
-     ~@body))
+  `(let [context# (ReqContext/context)
+         user# (if (.principal context#) (.getName (.principal context#)))]
+    (thrift/with-nimbus-connection-as-user
+       [~nimbus-sym (*STORM-CONF* NIMBUS-HOST) (*STORM-CONF* NIMBUS-THRIFT-PORT) user#]
+       ~@body)))
 
 (defn assert-authorized-user
   ([servlet-request op]
     (assert-authorized-user servlet-request op nil))
   ([servlet-request op topology-conf]
-     (if http-creds-handler (.populateContext http-creds-handler (ReqContext/context) servlet-request))
-     (if *UI-ACL-HANDLER*
-       (let [context (ReqContext/context)]
-         (if-not (.permit *UI-ACL-HANDLER* context op topology-conf)
-           (let [principal (.principal context)
-                 user (if principal (.getName principal) "unknown")]
-             (throw (AuthorizationException.
-                     (str "UI request '" op "' for '"
-                          user "' user is not authorized")))))))))
+    (let [context (ReqContext/context)]
+      (if http-creds-handler (.populateContext http-creds-handler context servlet-request))
+
+      (if (.isImpersonating context)
+        (if *UI-IMPERSONATION-HANDLER*
+            (if-not (.permit *UI-IMPERSONATION-HANDLER* context op topology-conf)
+              (let [principal (.principal context)
+                    real-principal (.realPrincipal context)
+                    user (if principal (.getName principal) "unknown")
+                    real-user (if real-principal (.getName real-principal) "unknown")
+                    remote-address (.remoteAddress context)]
+                (throw (AuthorizationException.
+                         (str "user '" real-user "' is not authorized to impersonate user
'" user "' from host '" remote-address "'. Please
+                         see SECURITY.MD to learn how to configure impersonation ACL.")))))
+          (log-warn " principal " (.realPrincipal context) " is trying to impersonate " (.principal
context) " but "
+            NIMBUS-IMPERSONATION-AUTHORIZER " has no authorizer configured. This is a potential
security hole.
+            Please see SECURITY.MD to learn how to configure an impersonation authorizer.")))
+
+      (if *UI-ACL-HANDLER*
+       (if-not (.permit *UI-ACL-HANDLER* context op topology-conf)
+         (let [principal (.principal context)
+               user (if principal (.getName principal) "unknown")]
+           (throw (AuthorizationException.
+                   (str "UI request '" op "' for '" user "' user is not authorized")))))))))
 
 (defn get-filled-stats
   [summs]
@@ -911,28 +929,29 @@
   (GET "/api/v1/token" [ & m]
        (json-response (format "{\"antiForgeryToken\": \"%s\"}" *anti-forgery-token*) (:callback
m) :serialize-fn identity))
   (POST "/api/v1/topology/:id/activate" [:as {:keys [cookies servlet-request]} id & m]
+    (assert-authorized-user servlet-request "activate" (topology-config id))
     (with-nimbus nimbus
       (let [tplg (->> (doto
                         (GetInfoOptions.)
                         (.set_num_err_choice NumErrorsChoice/NONE))
                       (.getTopologyInfoWithOpts ^Nimbus$Client nimbus id))
             name (.get_name tplg)]
-        (assert-authorized-user servlet-request "activate" (topology-config id))
         (.activate nimbus name)
         (log-message "Activating topology '" name "'")))
     (json-response (topology-op-response id "deactivate") (m "callback")))
   (POST "/api/v1/topology/:id/deactivate" [:as {:keys [cookies servlet-request]} id &
m]
+    (assert-authorized-user servlet-request "deactivate" (topology-config id))
     (with-nimbus nimbus
       (let [tplg (->> (doto
                         (GetInfoOptions.)
                         (.set_num_err_choice NumErrorsChoice/NONE))
                       (.getTopologyInfoWithOpts ^Nimbus$Client nimbus id))
             name (.get_name tplg)]
-        (assert-authorized-user servlet-request "deactivate" (topology-config id))
         (.deactivate nimbus name)
         (log-message "Deactivating topology '" name "'")))
     (json-response (topology-op-response id "deactivate") (m "callback")))
   (POST "/api/v1/topology/:id/rebalance/:wait-time" [:as {:keys [cookies servlet-request]}
id wait-time & m]
+    (assert-authorized-user servlet-request "rebalance" (topology-config id))
     (with-nimbus nimbus
       (let [tplg (->> (doto
                         (GetInfoOptions.)
@@ -941,7 +960,6 @@
             name (.get_name tplg)
             rebalance-options (m "rebalanceOptions")
             options (RebalanceOptions.)]
-        (assert-authorized-user servlet-request "rebalance" (topology-config id))
         (.set_wait_secs options (Integer/parseInt wait-time))
         (if (and (not-nil? rebalance-options) (contains? rebalance-options "numWorkers"))
           (.set_num_workers options (Integer/parseInt (.toString (rebalance-options "numWorkers")))))
@@ -952,6 +970,7 @@
         (log-message "Rebalancing topology '" name "' with wait time: " wait-time " secs")))
     (json-response (topology-op-response id "rebalance") (m "callback")))
   (POST "/api/v1/topology/:id/kill/:wait-time" [:as {:keys [cookies servlet-request]} id
wait-time & m]
+    (assert-authorized-user servlet-request "killTopology" (topology-config id))
     (with-nimbus nimbus
       (let [tplg (->> (doto
                         (GetInfoOptions.)
@@ -959,7 +978,6 @@
                       (.getTopologyInfoWithOpts ^Nimbus$Client nimbus id))
             name (.get_name tplg)
             options (KillOptions.)]
-        (assert-authorized-user servlet-request "killTopology" (topology-config id))
         (.set_wait_secs options (Integer/parseInt wait-time))
         (.killTopologyWithOpts nimbus name options)
         (log-message "Killing topology '" name "' with wait time: " wait-time " secs")))

http://git-wip-us.apache.org/repos/asf/storm/blob/e353fe00/storm-core/src/jvm/backtype/storm/security/auth/DefaultHttpCredentialsPlugin.java
----------------------------------------------------------------------
diff --git a/storm-core/src/jvm/backtype/storm/security/auth/DefaultHttpCredentialsPlugin.java
b/storm-core/src/jvm/backtype/storm/security/auth/DefaultHttpCredentialsPlugin.java
index 8645558..59c5b06 100644
--- a/storm-core/src/jvm/backtype/storm/security/auth/DefaultHttpCredentialsPlugin.java
+++ b/storm-core/src/jvm/backtype/storm/security/auth/DefaultHttpCredentialsPlugin.java
@@ -72,16 +72,24 @@ public class DefaultHttpCredentialsPlugin implements IHttpCredentialsPlugin
{
     public ReqContext populateContext(ReqContext context,
             HttpServletRequest req) {
         String userName = getUserName(req);
-        Principal p = null;
-        if (userName != null) {
-            p = new SingleUserPrincipal(userName);
+
+        String doAsUser = req.getHeader("doAsUser");
+        if(doAsUser == null) {
+            doAsUser = req.getParameter("doAsUser");
         }
-        Set<Principal> principals = new HashSet<Principal>(1);
-        if (p != null) {
-            principals.add(p);
+
+        if(doAsUser != null) {
+            context.setRealPrincipal(new SingleUserPrincipal(userName));
+            userName = doAsUser;
         }
-        Subject s = new Subject(true, principals, new HashSet(), new HashSet());
-        context.setSubject(s);
+
+        if(userName != null) {
+            Subject s = new Subject();
+            Principal p = new SingleUserPrincipal(userName);
+            s.getPrincipals().add(p);
+            context.setSubject(s);
+        }
+
         return context;
     }
 }


Mime
View raw message