You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by pr...@apache.org on 2018/08/22 08:40:06 UTC

zeppelin git commit: [ZEPPELIN-3741] Do not clear "Authorization" header if Z-server is running behind proxy

Repository: zeppelin
Updated Branches:
  refs/heads/master e10332c93 -> 3047bc2f5


[ZEPPELIN-3741] Do not clear "Authorization" header if Z-server is running behind proxy

There can be a case where Zeppelin-Sever is running as Form-Based-Authentication, however, it can be running behind a proxy which may be requiring Authorization header.
The idea of this PR is to not clear that header when it behind a proxy and control it with config.

[Bug Fix]

* [x] - Add documentaion

* [ZEPPELIN-3741](https://issues.apache.org/jira/browse/ZEPPELIN-3741)

* Configure Nginx to run with `auth_basic` option
* Start Zeppelin server behind a proxy server like Nginx
* Make sure that `shiro.ini` is configured to run with `/** = authc`
* In `zeppelin-site.xml` configure `zeppelin.server.authorization.header.clear` as `false`
Now on logout from Zeppelin-Server should not clear *Authorization* header of Nginx

* Does the licenses files need update? N/A
* Is there breaking changes for older versions? N/A
* Does this needs documentation? Yes

Author: Prabhjyot Singh <pr...@gmail.com>

Closes #3155 from prabhjyotsingh/ZEPPELIN-3741 and squashes the following commits:

d95fc32dc [Prabhjyot Singh] add documentation
54daaca8d [Prabhjyot Singh] rename variable to "zeppelin.server.authorization.header.clear"
832ef0481 [Prabhjyot Singh] ZEPPELIN-3741: Do not clear "Authorization" header if Z-server is running behind proxy

Change-Id: I8c504e170b576570dbb888160946a2c477d7928e


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

Branch: refs/heads/master
Commit: 3047bc2f595415560276832a8ca7ac178adff677
Parents: e10332c
Author: Prabhjyot Singh <pr...@gmail.com>
Authored: Tue Aug 21 18:57:19 2018 +0530
Committer: Prabhjyot Singh <pr...@gmail.com>
Committed: Wed Aug 22 14:09:57 2018 +0530

----------------------------------------------------------------------
 conf/zeppelin-site.xml.template                 |  8 +++
 docs/setup/security/shiro_authentication.md     |  6 ++
 .../zeppelin/conf/ZeppelinConfiguration.java    |  5 ++
 .../org/apache/zeppelin/rest/LoginRestApi.java  | 24 +++++--
 .../src/components/navbar/navbar.controller.js  | 74 +++++++++++---------
 5 files changed, 79 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/3047bc2f/conf/zeppelin-site.xml.template
----------------------------------------------------------------------
diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template
index 31f8f73..9d9a99f 100755
--- a/conf/zeppelin-site.xml.template
+++ b/conf/zeppelin-site.xml.template
@@ -486,6 +486,14 @@
 
 <!--
 <property>
+    <name>zeppelin.server.authorization.header.clear</name>
+    <value>true</value>
+    <description>Authorization header to be cleared if server is running as authcBasic</description>
+</property>
+-->
+
+<!--
+<property>
   <name>zeppelin.server.xframe.options</name>
   <value>SAMEORIGIN</value>
   <description>The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a frame/iframe/object.</description>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/3047bc2f/docs/setup/security/shiro_authentication.md
----------------------------------------------------------------------
diff --git a/docs/setup/security/shiro_authentication.md b/docs/setup/security/shiro_authentication.md
index 11d5c68..bb655f1 100644
--- a/docs/setup/security/shiro_authentication.md
+++ b/docs/setup/security/shiro_authentication.md
@@ -307,6 +307,12 @@ anyofrolesuser = org.apache.zeppelin.utils.AnyOfRolesUserAuthorizationFilter
 > **NOTE :** All of the above configurations are defined in the `conf/shiro.ini` file.
 
 
+## FAQ
+
+Zeppelin sever is configured as form-based authentication but is behind proxy configured as basic-authentication for example [NGINX](./authentication_nginx.html#http-basic-authentication-using-nginx) and don't want Zeppelin-Server to clear authentication headers. 
+
+> Set `zeppelin.server.authorization.header.clear` to `false` in zeppelin-site.xml
+
 ## Other authentication methods
 
 - [HTTP Basic Authentication using NGINX](./authentication_nginx.html)

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/3047bc2f/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
index df74283..2b2f3b6 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
@@ -571,6 +571,10 @@ public class ZeppelinConfiguration extends XMLConfiguration {
     return getInt(ConfVars.ZEPPELIN_SERVER_JETTY_REQUEST_HEADER_SIZE);
   }
 
+  public Boolean isAuthorizationHeaderClear() {
+    return getBoolean(ConfVars.ZEPPELIN_SERVER_AUTHORIZATION_HEADER_CLEAR);
+  }
+
 
   public String getXFrameOptions() {
     return getString(ConfVars.ZEPPELIN_SERVER_XFRAME_OPTIONS);
@@ -759,6 +763,7 @@ public class ZeppelinConfiguration extends XMLConfiguration {
     ZEPPELIN_SERVER_XFRAME_OPTIONS("zeppelin.server.xframe.options", "SAMEORIGIN"),
     ZEPPELIN_SERVER_JETTY_NAME("zeppelin.server.jetty.name", null),
     ZEPPELIN_SERVER_JETTY_REQUEST_HEADER_SIZE("zeppelin.server.jetty.request.header.size", 8192),
+    ZEPPELIN_SERVER_AUTHORIZATION_HEADER_CLEAR("zeppelin.server.authorization.header.clear", true),
     ZEPPELIN_SERVER_STRICT_TRANSPORT("zeppelin.server.strict.transport", "max-age=631138519"),
     ZEPPELIN_SERVER_X_XSS_PROTECTION("zeppelin.server.xxss.protection", "1"),
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/3047bc2f/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java
index 2937d02..f13c222 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java
@@ -17,6 +17,7 @@
 package org.apache.zeppelin.rest;
 
 import com.google.gson.Gson;
+import javax.inject.Inject;
 import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.IncorrectCredentialsException;
@@ -25,6 +26,8 @@ import org.apache.shiro.authc.UnknownAccountException;
 import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.realm.Realm;
 import org.apache.shiro.subject.Subject;
+import org.apache.zeppelin.conf.ZeppelinConfiguration;
+import org.apache.zeppelin.notebook.Notebook;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,6 +65,12 @@ import org.apache.zeppelin.utils.SecurityUtils;
 public class LoginRestApi {
   private static final Logger LOG = LoggerFactory.getLogger(LoginRestApi.class);
   private static final Gson gson = new Gson();
+  private ZeppelinConfiguration zConf;
+
+  @Inject
+  public LoginRestApi(Notebook notebook) {
+    this.zConf = notebook.getConf();
+  }
 
   @GET
   @ZeppelinApi
@@ -205,15 +214,22 @@ public class LoginRestApi {
   public Response logout() {
     JsonResponse response;
     logoutCurrentUser();
+    Status status = null;
+    Map<String, String> data = new HashMap<>();
+    if (zConf.isAuthorizationHeaderClear()) {
+      status = Status.UNAUTHORIZED;
+      data.put("clearAuthorizationHeader", "true");
+    } else {
+      status = Status.FORBIDDEN;
+      data.put("clearAuthorizationHeader", "false");
+    }
     if (isKnoxSSOEnabled()) {
       KnoxJwtRealm knoxJwtRealm = getJTWRealm();
-      Map<String, String> data = new HashMap<>();
       data.put("redirectURL", constructKnoxUrl(knoxJwtRealm, knoxJwtRealm.getLogout()));
       data.put("isLogoutAPI", knoxJwtRealm.getLogoutAPI().toString());
-      response = new JsonResponse(Status.UNAUTHORIZED, "", data);
+      response = new JsonResponse(status, "", data);
     } else {
-      response = new JsonResponse(Status.UNAUTHORIZED, "", "");
-
+      response = new JsonResponse(status, "", data);
     }
     LOG.warn(response.toString());
     return response.build();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/3047bc2f/zeppelin-web/src/components/navbar/navbar.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js
index 7665bf8..68a7f4a 100644
--- a/zeppelin-web/src/components/navbar/navbar.controller.js
+++ b/zeppelin-web/src/components/navbar/navbar.controller.js
@@ -91,6 +91,7 @@ function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
     let logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout';
 
     $http.post(logoutURL).then(function() {}, function(response) {
+      let clearAuthorizationHeader = 'true';
       if (response.data) {
         let res = angular.fromJson(response.data).body;
         if (res['redirectURL']) {
@@ -104,44 +105,49 @@ function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
           }
           return undefined;
         }
+        if (res['clearAuthorizationHeader']) {
+          clearAuthorizationHeader = res['clearAuthorizationHeader'];
+        }
       }
 
       // force authcBasic (if configured) to logout
-      if (detectIE()) {
-        let outcome;
-        try {
-          outcome = document.execCommand('ClearAuthenticationCache');
-        } catch (e) {
-          console.log(e);
-        }
-        if (!outcome) {
-          // Let's create an xmlhttp object
-          outcome = (function(x) {
-            if (x) {
-              // the reason we use "random" value for password is
-              // that browsers cache requests. changing
-              // password effectively behaves like cache-busing.
-              x.open('HEAD', location.href, true, 'logout',
-                (new Date()).getTime().toString());
-              x.send('');
-              // x.abort()
-              return 1; // this is **speculative** "We are done."
-            } else {
-              // eslint-disable-next-line no-useless-return
-              return;
-            }
-          })(window.XMLHttpRequest ? new window.XMLHttpRequest()
-            // eslint-disable-next-line no-undef
-            : (window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : u));
-        }
-        if (!outcome) {
-          let m = 'Your browser is too old or too weird to support log out functionality. Close all windows and ' +
-            'restart the browser.';
-          alert(m);
+      if (clearAuthorizationHeader === 'true') {
+        if (detectIE()) {
+          let outcome;
+          try {
+            outcome = document.execCommand('ClearAuthenticationCache');
+          } catch (e) {
+            console.log(e);
+          }
+          if (!outcome) {
+            // Let's create an xmlhttp object
+            outcome = (function(x) {
+              if (x) {
+                // the reason we use "random" value for password is
+                // that browsers cache requests. changing
+                // password effectively behaves like cache-busing.
+                x.open('HEAD', location.href, true, 'logout',
+                  (new Date()).getTime().toString());
+                x.send('');
+                // x.abort()
+                return 1; // this is **speculative** "We are done."
+              } else {
+                // eslint-disable-next-line no-useless-return
+                return;
+              }
+            })(window.XMLHttpRequest ? new window.XMLHttpRequest()
+              // eslint-disable-next-line no-undef
+              : (window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : u));
+          }
+          if (!outcome) {
+            let m = 'Your browser is too old or too weird to support log out functionality. Close all windows and ' +
+              'restart the browser.';
+            alert(m);
+          }
+        } else {
+          // for firefox and safari
+          logoutURL = logoutURL.replace('//', '//false:false@');
         }
-      } else {
-        // for firefox and safari
-        logoutURL = logoutURL.replace('//', '//false:false@');
       }
 
       $http.post(logoutURL).error(function() {