Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions conf/zeppelin-site.xml.template
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,14 @@
</property>
-->

<!--
<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>
Expand Down
6 changes: 6 additions & 0 deletions docs/setup/security/shiro_authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,10 @@ public Integer getJettyRequestHeaderSize() {
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);
Expand Down Expand Up @@ -759,6 +763,7 @@ public enum ConfVars {
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"),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,6 +26,8 @@
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;

Expand Down Expand Up @@ -62,6 +65,12 @@
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
Expand Down Expand Up @@ -205,15 +214,22 @@ public Response postLogin(@FormParam("userName") String userName,
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();
Expand Down
74 changes: 40 additions & 34 deletions zeppelin-web/src/components/navbar/navbar.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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']) {
Expand All @@ -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() {
Expand Down