You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@storm.apache.org by sr...@apache.org on 2015/03/12 18:06:51 UTC
[05/10] storm git commit: STORM-446: Adding doAsUser support to REST
APIs.
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 <br...@gmail.com>
Authored: Sat Feb 28 19:56:14 2015 -0800
Committer: Parth Brahmbhatt <br...@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;
}
}