Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ZEPPELIN-2598] Securing Zeppelin with OpenID Connect #2373

Closed
wants to merge 7 commits into from
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
15 changes: 7 additions & 8 deletions docs/setup/security/shiro_authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Change the following values in the Shiro.ini file, and uncomment the line:

### LDAP

Two options exist for configuring an LDAP Realm. The simpler to use is the LdapGroupRealm. How ever it has limited
Two options exist for configuring an LDAP Realm. The simpler to use is the LdapGroupRealm. How ever it has limited
flexibility with mapping of ldap groups to users and for authorization for user groups. A sample configuration file for
this realm is given below.

Expand All @@ -144,7 +144,7 @@ ldapRealm.contextFactory.authenticationMechanism = simple
The other more flexible option is to use the LdapRealm. It allows for mapping of ldapgroups to roles and also allows for
role/group based authentication into the zeppelin server. Sample configuration for this realm is given below.
```
[main]
[main]
ldapRealm=org.apache.zeppelin.realm.LdapRealm

ldapRealm.contextFactory.authenticationMechanism=simple
Expand All @@ -162,18 +162,18 @@ ldapRealm.groupObjectClass=groupofnames
ldapRealm.userSearchAttributeName = sAMAccountName
ldapRealm.memberAttribute=member
# force usernames returned from ldap to lowercase useful for AD
ldapRealm.userLowerCase = true
ldapRealm.userLowerCase = true
# ability set searchScopes subtree (default), one, base
ldapRealm.userSearchScope = subtree;
ldapRealm.groupSearchScope = subtree;
ldapRealm.memberAttributeValueTemplate=cn={0},ou=people,dc=hadoop,dc=apache,dc=org
ldapRealm.contextFactory.systemUsername=uid=guest,ou=people,dc=hadoop,dc=apache,dc=org
ldapRealm.contextFactory.systemUsername=uid=guest,ou=people,dc=hadoop,dc=apache,dc=org
ldapRealm.contextFactory.systemPassword=S{ALIAS=ldcSystemPassword}
# enable support for nested groups using the LDAP_MATCHING_RULE_IN_CHAIN operator
ldapRealm.groupSearchEnableMatchingRuleInChain = true
# optional mapping from physical groups to logical application roles
ldapRealm.rolesByGroup = LDN_USERS: user_role, NYK_USERS: user_role, HKG_USERS: user_role, GLOBAL_ADMIN: admin_role
# optional list of roles that are allowed to authenticate. Incase not present all groups are allowed to authenticate (login).
# optional list of roles that are allowed to authenticate. Incase not present all groups are allowed to authenticate (login).
# This changes nothing for url specific permissions that will continue to work as specified in [urls].
ldapRealm.allowedRolesForAuthentication = admin_role,user_role
ldapRealm.permissionsByRole= user_role = *:ToDoItemsJdo:*:*, *:ToDoItem:*:*; admin_role = *
Expand All @@ -182,12 +182,12 @@ securityManager.realms = $ldapRealm
```

### PAM
[PAM](https://en.wikipedia.org/wiki/Pluggable_authentication_module) authentication support allows the reuse of existing authentication
[PAM](https://en.wikipedia.org/wiki/Pluggable_authentication_module) authentication support allows the reuse of existing authentication
moduls on the host where Zeppelin is running. On a typical system modules are configured per service for example sshd, passwd, etc. under `/etc/pam.d/`. You can
either reuse one of these services or create your own for Zeppelin. Activiting PAM authentication requires two parameters:
1. realm: The Shiro realm being used
2. service: The service configured under `/etc/pam.d/` to be used. The name here needs to be the same as the file name under `/etc/pam.d/`

```
[main]
pamRealm=org.apache.zeppelin.realm.PamRealm
Expand Down Expand Up @@ -232,4 +232,3 @@ If you want to grant this permission to other users, you can change **roles[ ]**
## Other authentication methods

- [HTTP Basic Authentication using NGINX](./authentication_nginx.html)

2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
<commons.collections.version>3.2.1</commons.collections.version>
<commons.logging.version>1.1.1</commons.logging.version>
<commons.cli.version>1.3.1</commons.cli.version>
<shiro.version>1.2.3</shiro.version>
<shiro.version>1.3.2</shiro.version>

<!-- test library versions -->
<junit.version>4.12</junit.version>
Expand Down
4 changes: 2 additions & 2 deletions zeppelin-distribution/src/bin_license/LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ The following components are provided under Apache License.
(Apache 2.0) Lucene Suggest (org.apache.lucene:lucene-suggest:5.3.1 - http://lucene.apache.org/lucene-parent/lucene-suggest)
(Apache 2.0) Elasticsearch: Core (org.elasticsearch:elasticsearch:2.1.0 - http://nexus.sonatype.org/oss-repository-hosting.html/parent/elasticsearch)
(Apache 2.0) Joda convert (org.joda:joda-convert:1.8.1 - http://joda-convert.sourceforge.net)
(Apache 2.0) Shiro Core (org.apache.shiro:shiro-core:1.2.3 - https://shiro.apache.org)
(Apache 2.0) Shiro Web (org.apache.shiro:shiro-web:1.2.3 - https://shiro.apache.org)
(Apache 2.0) Shiro Core (org.apache.shiro:shiro-core:1.3.2 - https://shiro.apache.org)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@felixcheung already updated

(Apache 2.0) Shiro Web (org.apache.shiro:shiro-web:1.3.2 - https://shiro.apache.org)
(Apache 2.0) SnakeYAML (org.yaml:snakeyaml:1.15 - http://www.snakeyaml.org)
(Apache 2.0) Protocol Buffers (com.google.protobuf:protobuf-java:2.5.0 - https://github.com/google/protobuf/releases)
(Apache 2.0) Alluxio Shell (org.alluxio:alluxio-shell:1.0.0 - http://alluxio.org)
Expand Down
1 change: 0 additions & 1 deletion zeppelin-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,6 @@
</dependencies>
</dependencyManagement>
</profile>

<profile>
<id>using-source-tree</id>
<activation>
Expand Down
15 changes: 14 additions & 1 deletion zeppelin-web/src/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ let zeppelinWebApp = angular.module('zeppelinWebApp', requiredModules)

// handel logout on API failure
.config(function ($httpProvider, $provide) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
$provide.factory('httpInterceptor', function ($q, $rootScope) {
return {
'responseError': function (rejection) {
Expand Down Expand Up @@ -175,12 +176,24 @@ function auth () {
},
crossDomain: true
})
return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket').then(function (response) {
let config = {headers: { 'X-Requested-With': 'XMLHttpRequest' }}
return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket', config).then(function (response) {
zeppelinWebApp.run(function ($rootScope) {
$rootScope.ticket = angular.fromJson(response.data).body

$rootScope.ticket.screenUsername = $rootScope.ticket.principal
if ($rootScope.ticket.principal.startsWith('#Pac4j')) {
let re = ', name=(.*?),'
$rootScope.ticket.screenUsername = $rootScope.ticket.principal.match(re)[1]
}
})
}, function (errorResponse) {
// Handle error case
let redirect = errorResponse.headers('Location')
if (errorResponse.status === 401 && redirect !== undefined) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't redirect be error code 3xx instead of 4xx?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the redirect could not be performed automatically, since we are doing our requests with ajax.
In this case the answer is 401 https://github.com/pac4j/pac4j/blob/master/pac4j-core/src/main/java/org/pac4j/core/client/IndirectClient.java#L88

// Handle page redirect
window.location.href = redirect
}
})
}

Expand Down
2 changes: 2 additions & 0 deletions zeppelin-web/src/components/navbar/navbar.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ function NavCtrl ($scope, $rootScope, $http, $routeParams, $location,
$http.post(logoutURL).error(function () {
$rootScope.userName = ''
$rootScope.ticket.principal = ''
$rootScope.ticket.screenUsername = ''
$rootScope.ticket.ticket = ''
$rootScope.ticket.roles = ''
BootstrapDialog.show({
Expand Down Expand Up @@ -131,6 +132,7 @@ function NavCtrl ($scope, $rootScope, $http, $routeParams, $location,
})

$scope.$on('loginSuccess', function (event, param) {
$rootScope.ticket.screenUsername = $rootScope.ticket.principal
listConfigurations()
loadNotes()
getHomeNote()
Expand Down
2 changes: 1 addition & 1 deletion zeppelin-web/src/components/navbar/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
<i ng-if="!navbar.connected" class="fa fa-circle server-disconnected"
uib-tooltip="WebSocket Disconnected" tooltip-placement="bottom" style="margin-top: 7px; vertical-align: top"></i>
<button ng-if="ticket" class="nav-btn dropdown-toggle" type="button" data-toggle="dropdown" style="margin:11px 5px 0 0; padding-left: 0px;">
<span class="username">{{ticket.principal}}</span>
<span class="username">{{ticket.screenUsername}}</span>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and the reason for this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, buji library uses the principal field to store token information so we need to display proper user name instead of the whole content of the token.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that going to change the output for cases other than buji?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a filter with:

if ($rootScope.ticket.principal.startsWith("#Pac4j")) {

I hope is reasonably enough

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not "out of the box" but zeppelin could now support this integration

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it only limited to having pac4j on the classpath and necessary configuration in shiro.ini? Or does it required some code changes in Zeppelin as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pac4j and maybe other dependencies on classpath and configuration is enough! We are integrating with Keycloak i.e.

Copy link

@HarvinderBhullar HarvinderBhullar Jul 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried with following shiro.ini configuration with all the required jars on classpath. It is not working. I am getting following error

authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken] could not be authenticated by any configured realms.

sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager

securityManager.sessionManager = $sessionManager

oidcConfig = org.pac4j.oidc.config.OidcConfiguration
oidcConfig.discoveryURI = https://accounts.google.com/.well-known/openid-configuration
oidcConfig.clientId = 167480702619-8e1lo80dnu8bpk3k0lvvj27noin97vu9.apps.googleusercontent.com
oidcConfig.secret =MhMme_Ik6IH2JMnAT6MFIfee
oidcConfig.clientAuthenticationMethodAsString = client_secret_basic
oidcClient = org.pac4j.oidc.client.OidcClient
oidcClient.configuration = $oidcConfig

config = org.pac4j.core.config.Config

requireRoleAdmin = org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer
requireRoleAdmin.elements = admin

oidcSecurityFilter = io.buji.pac4j.filter.SecurityFilter
oidcSecurityFilter.config = $config
oidcSecurityFilter.clients = oidcClient
clients = org.pac4j.core.client.Clients
clients.callbackUrl = http://localhost:8086/api/callback
clients.clients = $oidcClient

config.clients = $clients
config.authorizers = admin:$requireRoleAdmin

pac4jRealm = io.buji.pac4j.realm.Pac4jRealm
pac4jSubjectFactory = io.buji.pac4j.subject.Pac4jSubjectFactory
securityManager.subjectFactory = $pac4jSubjectFactory

callbackFilter = io.buji.pac4j.filter.CallbackFilter
callbackFilter.defaultUrl = http://localhost:8086
callbackFilter.config = $config

securityManager.sessionManager.globalSessionTimeout = 86400000
shiro.loginUrl = /api/login

[urls]
/api/callback = callbackFilter

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am setting up Google OpenID client, the login on Zeppelin should take me to Google account but it is opening Zeppelin Login form, what setup is needed in shiro.ini to correct this behavior?

<span class="caret" style="margin-bottom: 8px"></span>
</button>
<span ng-if="!ticket" style="margin: 5px;"></span>
Expand Down