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 <[email protected]> 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 <[email protected]> Authored: Tue Aug 21 18:57:19 2018 +0530 Committer: Prabhjyot Singh <[email protected]> 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() {
