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 2016/06/17 09:19:11 UTC

zeppelin git commit: [Zeppelin 946] Permissions not honoring group

Repository: zeppelin
Updated Branches:
  refs/heads/master 1c3373937 -> 24922e103


[Zeppelin 946] Permissions not honoring group

### What is this PR for?
Error:
Insufficient privileges to write notebook.
Allowed users or roles: [admin, zeppelinWrite]
But the user randerson belongs to: [randerson]
It's seems clear that user randerson isn't mapped to any roles, or groups (even though he of course is a member of the zeppelinWrite group in AD and as a result also part of the local admin Role). A TCPDUMP reveals that during login, all of my group memberships are in fact returned during the ldap bind operation. However, when I attempt to modify a notebook, a call is never made to AD, to pull back my group memberships. It doesn't seem to look at my local group memberships (/etc/group) either.

### What type of PR is it?
[Bug Fix]

### Todos
* [x] - fix for permissions not honoring group
* [x] - read roles from shiro.ini
* [x] - at times group name was displaying instead of user/principal name.
* [x] - doc

### What is the Jira issue?
[ZEPPELIN-946](https://issues.apache.org/jira/browse/ZEPPELIN-946)

### Screenshots/How should this be tested?
Use one of the following setting for IniRealm, LDAP or AD in shiro.ini

    [main]
    admin = password1, admin
    finance1 = finance1, finance
    finance2 = finance2, finance
    hr1 = hr1, hr
    hr2 = hr2, hr

    activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm
    activeDirectoryRealm.systemUsername = userNameA
    activeDirectoryRealm.systemPassword = passwordA
    activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM
    activeDirectoryRealm.url = ldap://ldap.test.com:389
    activeDirectoryRealm.groupRolesMap = "CN=admin,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"admin","CN=finance,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"finance","CN=hr,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"hr"
    activeDirectoryRealm.authorizationCachingEnabled = false

    ldapRealm = org.apache.zeppelin.server.LdapGroupRealm
    # search base for ldap groups (only relevant for LdapGroupRealm):
    ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM
    ldapRealm.contextFactory.url = ldap://ldap.test.com:389
    ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM
    ldapRealm.contextFactory.authenticationMechanism = SIMPLE

    [roles]
    admin = *
    hr = *
    finance = *
    group1 = *

    [urls]
    /api/version = anon
    /** = authc

Login as user1 (say finance1), and set a permission of a notebook as "finance"
<img width="1282" alt="screen shot 2016-06-11 at 9 50 32 am" src="https://cloud.githubusercontent.com/assets/674497/15983178/aad710ee-2fbc-11e6-861d-508ecc8c7b74.png">

Save setting
<img width="1281" alt="screen shot 2016-06-11 at 9 51 05 am" src="https://cloud.githubusercontent.com/assets/674497/15983180/aad86ea8-2fbc-11e6-8b68-4571496ec733.png">

Now logout and login as user2 (say finance2) which belong to the same group as above "finance", verify that you have access to the same notebook.
<img width="1282" alt="screen shot 2016-06-11 at 9 51 25 am" src="https://cloud.githubusercontent.com/assets/674497/15983181/aad9a78c-2fbc-11e6-8a41-a3dc108cabdc.png">

Logout and login again, this time as a user that does not belong to the group "finance", a user say hr1. Verify that this user does not have permission to view the same notebook.
<img width="1281" alt="screen shot 2016-06-11 at 9 51 42 am" src="https://cloud.githubusercontent.com/assets/674497/15983179/aad7794e-2fbc-11e6-9002-f7b0fc54ac59.png">

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no

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

Closes #986 from prabhjyotsingh/ZEPPELIN-946 and squashes the following commits:

e04c145 [Prabhjyot Singh] add sample LDAP and AD realm setting in comments
3e443d7 [Prabhjyot Singh] imporoving performance of ActiveDirectoryGroupRealm
188ac17 [Prabhjyot Singh] activeDirectoryRealm.principalSuffix isn't honoured
293853e [Prabhjyot Singh] fix failing selenium test case
8d41149 [Prabhjyot Singh] try maximize browser
41bb23b [Prabhjyot Singh] selenium test case
3149417 [Prabhjyot Singh] Merge remote-tracking branch 'origin/master' into ZEPPELIN-946
310a81d [Prabhjyot Singh] make `[roles]` optional in shiro.ini
966a96c [Prabhjyot Singh] update doc
ed54a92 [Prabhjyot Singh] read roles from shiro.ini
e8f1f97 [Prabhjyot Singh] fix for permissions not honoring group
4194f93 [Prabhjyot Singh] sometime it dispalys groupName instead of principal


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

Branch: refs/heads/master
Commit: 24922e1036c5e410b676fd9b513d008cb046424e
Parents: 1c33739
Author: Prabhjyot Singh <pr...@gmail.com>
Authored: Fri Jun 17 07:35:21 2016 +0530
Committer: Prabhjyot Singh <pr...@gmail.com>
Committed: Fri Jun 17 14:48:57 2016 +0530

----------------------------------------------------------------------
 conf/shiro.ini                                  |  31 ++-
 docs/security/shiroauthentication.md            |  32 +++
 .../apache/zeppelin/rest/SecurityRestApi.java   |  20 +-
 .../server/ActiveDirectoryGroupRealm.java       | 241 +++++++++++++++++++
 .../apache/zeppelin/server/LdapGroupRealm.java  |  94 ++++++++
 .../apache/zeppelin/socket/NotebookServer.java  |  43 ++--
 .../apache/zeppelin/utils/SecurityUtils.java    |  49 +++-
 .../org/apache/zeppelin/AbstractZeppelinIT.java |   4 +-
 .../org/apache/zeppelin/WebDriverManager.java   |   3 +-
 .../org/apache/zeppelin/ZeppelinITUtils.java    |   7 +
 .../zeppelin/integration/AuthenticationIT.java  | 209 ++++++++++++++++
 .../src/app/notebook/notebook-actionBar.html    |  21 +-
 12 files changed, 700 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/conf/shiro.ini
----------------------------------------------------------------------
diff --git a/conf/shiro.ini b/conf/shiro.ini
index 61ee964..ced9776 100644
--- a/conf/shiro.ini
+++ b/conf/shiro.ini
@@ -25,19 +25,44 @@ user3 = password4, role2
 
 # Sample LDAP configuration, for user Authentication, currently tested for single Realm
 [main]
-#ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
-#ldapRealm.userDnTemplate = cn={0},cn=engg,ou=testdomain,dc=testdomain,dc=com
-#ldapRealm.contextFactory.url = ldap://ldaphost:389
+### A sample for configuring Active Directory Realm
+#activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm
+#activeDirectoryRealm.systemUsername = userNameA
+#activeDirectoryRealm.systemPassword = passwordA
+#activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM
+#activeDirectoryRealm.url = ldap://ldap.test.com:389
+#activeDirectoryRealm.groupRolesMap = "CN=admin,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"admin","CN=finance,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"finance","CN=hr,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"hr"
+#activeDirectoryRealm.authorizationCachingEnabled = false
+
+### A sample for configuring LDAP Directory Realm
+#ldapRealm = org.apache.zeppelin.server.LdapGroupRealm
+## search base for ldap groups (only relevant for LdapGroupRealm):
+#ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM
+#ldapRealm.contextFactory.url = ldap://ldap.test.com:389
+#ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM
 #ldapRealm.contextFactory.authenticationMechanism = SIMPLE
+
+
 sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
+
+### If caching of user is required then uncomment below lines
+#cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
+#securityManager.cacheManager = $cacheManager
+
 securityManager.sessionManager = $sessionManager
 # 86,400,000 milliseconds = 24 hour
 securityManager.sessionManager.globalSessionTimeout = 86400000
 shiro.loginUrl = /api/login
 
+[roles]
+role1 = *
+role2 = *
+role3 = *
+
 [urls]
 # anon means the access is anonymous.
 # authcBasic means Basic Auth Security
+# authc means Form based Auth Security
 # To enfore security, comment the line below and uncomment the next one
 /api/version = anon
 /** = anon

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/docs/security/shiroauthentication.md
----------------------------------------------------------------------
diff --git a/docs/security/shiroauthentication.md b/docs/security/shiroauthentication.md
index 646d740..969e2f4 100644
--- a/docs/security/shiroauthentication.md
+++ b/docs/security/shiroauthentication.md
@@ -69,4 +69,36 @@ user2 = password3
 
 Those combinations are defined in the `conf/shiro.ini` file.
 
+####5. Groups and permissions (optional)
+In case you want to leverage user groups and permissions, use one of the following configuration for LDAP or AD under `[main]` segment of shiro.ini
+
+```
+activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm
+activeDirectoryRealm.systemUsername = userNameA
+activeDirectoryRealm.systemPassword = passwordA
+activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM
+activeDirectoryRealm.url = ldap://ldap.test.com:389
+activeDirectoryRealm.groupRolesMap = "CN=aGroupName,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"group1"
+activeDirectoryRealm.authorizationCachingEnabled = false
+
+ldapRealm = org.apache.zeppelin.server.LdapGroupRealm
+# search base for ldap groups (only relevant for LdapGroupRealm):
+ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM
+ldapRealm.contextFactory.url = ldap://ldap.test.com:389
+ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM
+ldapRealm.contextFactory.authenticationMechanism = SIMPLE
+```
+ 
+also define roles/groups that you want to have in system, like below;
+
+```
+[roles]
+admin = *
+hr = *
+finance = *
+group1 = *
+```
+
+All of above configurations are defined in the `conf/shiro.ini` file.
+
 > **NOTE :** This documentation is originally from [SECURITY-README.md](https://github.com/apache/zeppelin/blob/master/SECURITY-README.md).

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java
index e344956..11c8f96 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java
@@ -22,8 +22,6 @@ import org.apache.shiro.realm.Realm;
 import org.apache.shiro.realm.jdbc.JdbcRealm;
 import org.apache.shiro.realm.ldap.JndiLdapRealm;
 import org.apache.shiro.realm.text.IniRealm;
-import org.apache.shiro.util.ThreadContext;
-import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
 import org.apache.zeppelin.annotation.ZeppelinApi;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.server.JsonResponse;
@@ -41,7 +39,6 @@ import java.util.*;
 
 /**
  * Zeppelin security rest api endpoint.
- *
  */
 @Path("/security")
 @Produces("application/json")
@@ -101,19 +98,16 @@ public class SecurityRestApi {
     List<String> usersList = new ArrayList<>();
     try {
       GetUserList getUserListObj = new GetUserList();
-      DefaultWebSecurityManager defaultWebSecurityManager;
-      String key = ThreadContext.SECURITY_MANAGER_KEY;
-      defaultWebSecurityManager = (DefaultWebSecurityManager) ThreadContext.get(key);
-      Collection<Realm> realms = defaultWebSecurityManager.getRealms();
-      List realmsList = new ArrayList(realms);
-      for (int i = 0; i < realmsList.size(); i++) {
-        String name = ((Realm) realmsList.get(i)).getName();
+      Collection realmsList = SecurityUtils.getRealmsList();
+      for (Iterator<Realm> iterator = realmsList.iterator(); iterator.hasNext(); ) {
+        Realm realm = iterator.next();
+        String name = realm.getName();
         if (name.equals("iniRealm")) {
-          usersList.addAll(getUserListObj.getUserList((IniRealm) realmsList.get(i)));
+          usersList.addAll(getUserListObj.getUserList((IniRealm) realm));
         } else if (name.equals("ldapRealm")) {
-          usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realmsList.get(i)));
+          usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm));
         } else if (name.equals("jdbcRealm")) {
-          usersList.addAll(getUserListObj.getUserList((JdbcRealm) realmsList.get(i)));
+          usersList.addAll(getUserListObj.getUserList((JdbcRealm) realm));
         }
       }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java
new file mode 100644
index 0000000..fc3ccc8
--- /dev/null
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zeppelin.server;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.realm.ldap.AbstractLdapRealm;
+import org.apache.shiro.realm.ldap.LdapContextFactory;
+import org.apache.shiro.realm.ldap.LdapUtils;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.*;
+
+
+/**
+ * A {@link Realm} that authenticates with an active directory LDAP
+ * server to determine the roles for a particular user.  This implementation
+ * queries for the user's groups and then maps the group names to roles using the
+ * {@link #groupRolesMap}.
+ *
+ * @since 0.1
+ */
+public class ActiveDirectoryGroupRealm extends AbstractLdapRealm {
+
+  private static final Logger log = LoggerFactory.getLogger(ActiveDirectoryGroupRealm.class);
+
+  private static final String ROLE_NAMES_DELIMETER = ",";
+
+    /*--------------------------------------------
+    |    I N S T A N C E   V A R I A B L E S    |
+    ============================================*/
+
+  /**
+   * Mapping from fully qualified active directory
+   * group names (e.g. CN=Group,OU=Company,DC=MyDomain,DC=local)
+   * as returned by the active directory LDAP server to role names.
+   */
+  private Map<String, String> groupRolesMap;
+
+    /*--------------------------------------------
+    |         C O N S T R U C T O R S           |
+    ============================================*/
+
+  public void setGroupRolesMap(Map<String, String> groupRolesMap) {
+    this.groupRolesMap = groupRolesMap;
+  }
+
+    /*--------------------------------------------
+    |               M E T H O D S               |
+    ============================================*/
+
+
+  /**
+   * Builds an {@link AuthenticationInfo} object by querying the active directory LDAP context for
+   * the specified username.  This method binds to the LDAP server using the provided username
+   * and password - which if successful, indicates that the password is correct.
+   * <p/>
+   * This method can be overridden by subclasses to query the LDAP server in a more complex way.
+   *
+   * @param token              the authentication token provided by the user.
+   * @param ldapContextFactory the factory used to build connections to the LDAP server.
+   * @return an {@link AuthenticationInfo} instance containing information retrieved from LDAP.
+   * @throws NamingException if any LDAP errors occur during the search.
+   */
+  protected AuthenticationInfo queryForAuthenticationInfo(
+      AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException {
+
+    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
+
+    // Binds using the username and password provided by the user.
+    LdapContext ctx = null;
+    try {
+      String userPrincipalName = upToken.getUsername();
+      if (userPrincipalName == null) {
+        return null;
+      }
+      if (this.principalSuffix != null) {
+        userPrincipalName = upToken.getUsername() + this.principalSuffix;
+      }
+      ctx = ldapContextFactory.getLdapContext(
+          userPrincipalName, upToken.getPassword());
+    } finally {
+      LdapUtils.closeContext(ctx);
+    }
+
+    return buildAuthenticationInfo(upToken.getUsername(), upToken.getPassword());
+  }
+
+  protected AuthenticationInfo buildAuthenticationInfo(String username, char[] password) {
+    return new SimpleAuthenticationInfo(username, password, getName());
+  }
+
+
+  /**
+   * Builds an {@link org.apache.shiro.authz.AuthorizationInfo} object by querying the active
+   * directory LDAP context for the groups that a user is a member of.  The groups are then
+   * translated to role names by using the configured {@link #groupRolesMap}.
+   * <p/>
+   * This implementation expects the <tt>principal</tt> argument to be a String username.
+   * <p/>
+   * Subclasses can override this method to determine authorization data (roles, permissions, etc)
+   * in a more complex way.  Note that this default implementation does not support permissions,
+   * only roles.
+   *
+   * @param principals         the principal of the Subject whose account is being retrieved.
+   * @param ldapContextFactory the factory used to create LDAP connections.
+   * @return the AuthorizationInfo for the given Subject principal.
+   * @throws NamingException if an error occurs when searching the LDAP server.
+   */
+  protected AuthorizationInfo queryForAuthorizationInfo(
+      PrincipalCollection principals,
+      LdapContextFactory ldapContextFactory) throws NamingException {
+
+    String username = (String) getAvailablePrincipal(principals);
+
+    // Perform context search
+    LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();
+
+    Set<String> roleNames;
+
+    try {
+      roleNames = getRoleNamesForUser(username, ldapContext);
+    } finally {
+      LdapUtils.closeContext(ldapContext);
+    }
+
+    return buildAuthorizationInfo(roleNames);
+  }
+
+  protected AuthorizationInfo buildAuthorizationInfo(Set<String> roleNames) {
+    return new SimpleAuthorizationInfo(roleNames);
+  }
+
+  private Set<String> getRoleNamesForUser(String username, LdapContext ldapContext)
+      throws NamingException {
+    Set<String> roleNames = new LinkedHashSet<>();
+
+    SearchControls searchCtls = new SearchControls();
+    searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+    String userPrincipalName = username;
+    if (principalSuffix != null) {
+      userPrincipalName += principalSuffix;
+    }
+
+    String searchFilter = "(&(objectClass=*)(userPrincipalName=" + userPrincipalName + "))";
+    Object[] searchArguments = new Object[]{userPrincipalName};
+
+    NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments,
+        searchCtls);
+
+    while (answer.hasMoreElements()) {
+      SearchResult sr = (SearchResult) answer.next();
+
+      if (log.isDebugEnabled()) {
+        log.debug("Retrieving group names for user [" + sr.getName() + "]");
+      }
+
+      Attributes attrs = sr.getAttributes();
+
+      if (attrs != null) {
+        NamingEnumeration ae = attrs.getAll();
+        while (ae.hasMore()) {
+          Attribute attr = (Attribute) ae.next();
+
+          if (attr.getID().equals("memberOf")) {
+
+            Collection<String> groupNames = LdapUtils.getAllAttributeValues(attr);
+
+            if (log.isDebugEnabled()) {
+              log.debug("Groups found for user [" + username + "]: " + groupNames);
+            }
+
+            Collection<String> rolesForGroups = getRoleNamesForGroups(groupNames);
+            roleNames.addAll(rolesForGroups);
+          }
+        }
+      }
+    }
+    return roleNames;
+  }
+
+  /**
+   * This method is called by the default implementation to translate Active Directory group names
+   * to role names.  This implementation uses the {@link #groupRolesMap} to map group names to role
+   * names.
+   *
+   * @param groupNames the group names that apply to the current user.
+   * @return a collection of roles that are implied by the given role names.
+   */
+  protected Collection<String> getRoleNamesForGroups(Collection<String> groupNames) {
+    Set<String> roleNames = new HashSet<String>(groupNames.size());
+
+    if (groupRolesMap != null) {
+      for (String groupName : groupNames) {
+        String strRoleNames = groupRolesMap.get(groupName);
+        if (strRoleNames != null) {
+          for (String roleName : strRoleNames.split(ROLE_NAMES_DELIMETER)) {
+
+            if (log.isDebugEnabled()) {
+              log.debug("User is member of group [" + groupName + "] so adding role [" +
+                  roleName + "]");
+            }
+
+            roleNames.add(roleName);
+
+          }
+        }
+      }
+    }
+    return roleNames;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java
new file mode 100644
index 0000000..a718c77
--- /dev/null
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zeppelin.server;
+
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.ldap.JndiLdapRealm;
+import org.apache.shiro.realm.ldap.LdapContextFactory;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+
+/**
+ * Created for org.apache.zeppelin.server on 09/06/16.
+ */
+public class LdapGroupRealm extends JndiLdapRealm {
+  private static final Logger LOG = LoggerFactory.getLogger(LdapGroupRealm.class);
+
+  public AuthorizationInfo queryForAuthorizationInfo(
+      PrincipalCollection principals,
+      LdapContextFactory ldapContextFactory) throws NamingException {
+    String username = (String) getAvailablePrincipal(principals);
+    LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();
+    Set<String> roleNames = getRoleNamesForUser(username, ldapContext, getUserDnTemplate());
+    return new SimpleAuthorizationInfo(roleNames);
+  }
+
+
+  public Set<String> getRoleNamesForUser(String username,
+                                         LdapContext ldapContext,
+                                         String userDnTemplate) throws NamingException {
+    try {
+      Set<String> roleNames = new LinkedHashSet<String>();
+
+      SearchControls searchCtls = new SearchControls();
+      searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+      String searchFilter = "(&(objectClass=groupOfNames)(member=" + userDnTemplate + "))";
+      Object[] searchArguments = new Object[]{username};
+
+      NamingEnumeration<?> answer = ldapContext.search(
+          String.valueOf(ldapContext.getEnvironment().get("ldap.searchBase")),
+          searchFilter,
+          searchArguments,
+          searchCtls);
+
+      while (answer.hasMoreElements()) {
+        SearchResult sr = (SearchResult) answer.next();
+        Attributes attrs = sr.getAttributes();
+        if (attrs != null) {
+          NamingEnumeration<?> ae = attrs.getAll();
+          while (ae.hasMore()) {
+            Attribute attr = (Attribute) ae.next();
+            if (attr.getID().equals("cn")) {
+              roleNames.add((String) attr.get());
+            }
+          }
+        }
+      }
+      return roleNames;
+
+    } catch (Exception e) {
+      LOG.error("Error", e);
+    }
+
+    return new HashSet<>();
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index dc190d1..20e8d5a 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -157,7 +157,7 @@ public class NotebookServer extends WebSocketServlet implements
             broadcastReloadedNoteList();
             break;
           case GET_HOME_NOTE:
-            sendHomeNote(conn, userAndRoles, notebook);
+            sendHomeNote(conn, userAndRoles, notebook, messagereceived);
             break;
           case GET_NOTE:
             sendNote(conn, userAndRoles, notebook, messagereceived);
@@ -451,13 +451,13 @@ public class NotebookServer extends WebSocketServlet implements
     broadcastAll(new Message(OP.NOTES_INFO).put("notes", notesInfo));
   }
 
-  void permissionError(NotebookSocket conn, String op, Set<String> userAndRoles,
+  void permissionError(NotebookSocket conn, String op,
+                       String userName,
+                       Set<String> userAndRoles,
                        Set<String> allowed) throws IOException {
     LOG.info("Cannot {}. Connection readers {}. Allowed readers {}",
             op, userAndRoles, allowed);
 
-    String userName = userAndRoles.iterator().next();
-
     conn.send(serializeMessage(new Message(OP.AUTH_INFO).put("info",
             "Insufficient privileges to " + op + " notebook.\n\n" +
                     "Allowed users or roles: " + allowed.toString() + "\n\n" +
@@ -481,7 +481,8 @@ public class NotebookServer extends WebSocketServlet implements
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (note != null) {
       if (!notebookAuthorization.isReader(noteId, userAndRoles)) {
-        permissionError(conn, "read", userAndRoles, notebookAuthorization.getReaders(noteId));
+        permissionError(conn, "read", fromMessage.principal, userAndRoles,
+            notebookAuthorization.getReaders(noteId));
         return;
       }
       addConnectionToNote(note.id(), conn);
@@ -491,7 +492,7 @@ public class NotebookServer extends WebSocketServlet implements
   }
 
   private void sendHomeNote(NotebookSocket conn, HashSet<String> userAndRoles,
-                            Notebook notebook) throws IOException {
+                            Notebook notebook, Message fromMessage) throws IOException {
     String noteId = notebook.getConf().getString(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN);
 
     Note note = null;
@@ -502,7 +503,8 @@ public class NotebookServer extends WebSocketServlet implements
     if (note != null) {
       NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
       if (!notebookAuthorization.isReader(noteId, userAndRoles)) {
-        permissionError(conn, "read", userAndRoles, notebookAuthorization.getReaders(noteId));
+        permissionError(conn, "read", fromMessage.principal,
+            userAndRoles, notebookAuthorization.getReaders(noteId));
         return;
       }
       addConnectionToNote(note.id(), conn);
@@ -530,7 +532,8 @@ public class NotebookServer extends WebSocketServlet implements
 
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "update", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "update", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 
@@ -593,7 +596,8 @@ public class NotebookServer extends WebSocketServlet implements
     Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isOwner(noteId, userAndRoles)) {
-      permissionError(conn, "remove", userAndRoles, notebookAuthorization.getOwners(noteId));
+      permissionError(conn, "remove", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getOwners(noteId));
       return;
     }
 
@@ -617,7 +621,8 @@ public class NotebookServer extends WebSocketServlet implements
     final Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "write", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 
@@ -666,7 +671,8 @@ public class NotebookServer extends WebSocketServlet implements
     final Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "write", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 
@@ -688,7 +694,8 @@ public class NotebookServer extends WebSocketServlet implements
     final Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "write", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 
@@ -968,7 +975,8 @@ public class NotebookServer extends WebSocketServlet implements
     final Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "write", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 
@@ -985,7 +993,8 @@ public class NotebookServer extends WebSocketServlet implements
     final Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "write", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 
@@ -1005,7 +1014,8 @@ public class NotebookServer extends WebSocketServlet implements
     final Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "write", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 
@@ -1024,7 +1034,8 @@ public class NotebookServer extends WebSocketServlet implements
     final Note note = notebook.getNote(noteId);
     NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
     if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
-      permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
+      permissionError(conn, "write", fromMessage.principal,
+          userAndRoles, notebookAuthorization.getWriters(noteId));
       return;
     }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java
index e7e39f2..4de4573 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java
@@ -16,15 +16,18 @@
  */
 package org.apache.zeppelin.utils;
 
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.realm.text.IniRealm;
 import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.ThreadContext;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.HashSet;
+import java.util.*;
 
 /**
  * Tools for securing Zeppelin
@@ -33,7 +36,7 @@ public class SecurityUtils {
 
   public static Boolean isValidOrigin(String sourceHost, ZeppelinConfiguration conf)
       throws UnknownHostException, URISyntaxException {
-    if (sourceHost == null || sourceHost.isEmpty()){
+    if (sourceHost == null || sourceHost.isEmpty()) {
       return false;
     }
     String sourceUriHost = new URI(sourceHost).getHost();
@@ -43,13 +46,14 @@ public class SecurityUtils {
     String currentHost = InetAddress.getLocalHost().getHostName().toLowerCase();
 
     return conf.getAllowedOrigins().contains("*") ||
-            currentHost.equals(sourceUriHost) ||
-            "localhost".equals(sourceUriHost) ||
-            conf.getAllowedOrigins().contains(sourceHost);
+        currentHost.equals(sourceUriHost) ||
+        "localhost".equals(sourceUriHost) ||
+        conf.getAllowedOrigins().contains(sourceHost);
   }
 
   /**
    * Return the authenticated user if any otherwise returns "anonymous"
+   *
    * @return shiro principal
    */
   public static String getPrincipal() {
@@ -58,26 +62,49 @@ public class SecurityUtils {
     String principal;
     if (subject.isAuthenticated()) {
       principal = subject.getPrincipal().toString();
-    }
-    else {
+    } else {
       principal = "anonymous";
     }
     return principal;
   }
 
+  public static Collection getRealmsList() {
+    DefaultWebSecurityManager defaultWebSecurityManager;
+    String key = ThreadContext.SECURITY_MANAGER_KEY;
+    defaultWebSecurityManager = (DefaultWebSecurityManager) ThreadContext.get(key);
+    Collection<Realm> realms = defaultWebSecurityManager.getRealms();
+    return realms;
+  }
+
   /**
    * Return the roles associated with the authenticated user if any otherwise returns empty set
    * TODO(prasadwagle) Find correct way to get user roles (see SHIRO-492)
+   *
    * @return shiro roles
    */
   public static HashSet<String> getRoles() {
     Subject subject = org.apache.shiro.SecurityUtils.getSubject();
     HashSet<String> roles = new HashSet<>();
+    Map allRoles = null;
 
     if (subject.isAuthenticated()) {
-      for (String role : Arrays.asList("role1", "role2", "role3")) {
-        if (subject.hasRole(role)) {
-          roles.add(role);
+      Collection realmsList = SecurityUtils.getRealmsList();
+      for (Iterator<Realm> iterator = realmsList.iterator(); iterator.hasNext(); ) {
+        Realm realm = iterator.next();
+        String name = realm.getName();
+        if (name.equals("iniRealm")) {
+          allRoles = ((IniRealm) realm).getIni().get("roles");
+          break;
+        }
+      }
+
+      if (allRoles != null) {
+        Iterator it = allRoles.entrySet().iterator();
+        while (it.hasNext()) {
+          Map.Entry pair = (Map.Entry) it.next();
+          if (subject.hasRole((String) pair.getKey())) {
+            roles.add((String) pair.getKey());
+          }
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
index a86d08b..3e56747 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
@@ -38,7 +38,7 @@ import static org.openqa.selenium.Keys.ENTER;
 import static org.openqa.selenium.Keys.SHIFT;
 
 abstract public class AbstractZeppelinIT {
-  protected WebDriver driver;
+  protected static WebDriver driver;
 
   protected final static Logger LOG = LoggerFactory.getLogger(AbstractZeppelinIT.class);
   protected static final long MAX_IMPLICIT_WAIT = 30;
@@ -114,7 +114,7 @@ abstract public class AbstractZeppelinIT {
     });
   }
 
-  protected boolean endToEndTestEnabled() {
+  protected static boolean endToEndTestEnabled() {
     return null != System.getenv("TEST_SELENIUM");
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java b/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java
index 4f0f394..49d6f1e 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java
@@ -125,7 +125,8 @@ public class WebDriverManager {
         (new WebDriverWait(driver, 5)).until(new ExpectedCondition<Boolean>() {
           @Override
           public Boolean apply(WebDriver d) {
-            return d.findElement(By.partialLinkText("Create new note"))
+            return d.findElement(By.xpath(
+                "//div[contains(@class, 'navbar-collapse')]//li//a[contains(.,'Connected')]"))
                 .isDisplayed();
           }
         });

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java
index 9800df6..46ffbe7 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java
@@ -39,4 +39,11 @@ public class ZeppelinITUtils {
       LOG.info("Finished.");
     }
   }
+
+  public static void restartZeppelin() {
+    CommandExecutor.executeCommandLocalHost("../bin/zeppelin-daemon.sh restart",
+        false, ProcessData.Types_Of_Data.OUTPUT);
+    //wait for server to start.
+    sleep(5000, false);
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java
new file mode 100644
index 0000000..3b1088e
--- /dev/null
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zeppelin.integration;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.zeppelin.AbstractZeppelinIT;
+import org.apache.zeppelin.WebDriverManager;
+import org.apache.zeppelin.ZeppelinITUtils;
+import org.apache.zeppelin.conf.ZeppelinConfiguration;
+import org.hamcrest.CoreMatchers;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+
+/**
+ * Created for org.apache.zeppelin.integration on 13/06/16.
+ */
+public class AuthenticationIT extends AbstractZeppelinIT {
+  private static final Logger LOG = LoggerFactory.getLogger(AuthenticationIT.class);
+
+  @Rule
+  public ErrorCollector collector = new ErrorCollector();
+
+  static String authShiro = "[users]\n" +
+      "admin = password1, admin\n" +
+      "finance1 = finance1, finance\n" +
+      "finance2 = finance2, finance\n" +
+      "hr1 = hr1, hr\n" +
+      "hr2 = hr2, hr\n" +
+      "[main]\n" +
+      "sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager\n" +
+      "securityManager.sessionManager = $sessionManager\n" +
+      "securityManager.sessionManager.globalSessionTimeout = 86400000\n" +
+      "shiro.loginUrl = /api/login\n" +
+      "[roles]\n" +
+      "admin = *\n" +
+      "hr = *\n" +
+      "finance = *\n" +
+      "[urls]\n" +
+      "/api/version = anon\n" +
+      "/** = authc";
+
+  static String originalShiro = "";
+
+
+  @BeforeClass
+  public static void startUp() {
+    if (!endToEndTestEnabled()) {
+      return;
+    }
+
+    try {
+      ZeppelinConfiguration conf = ZeppelinConfiguration.create();
+      File file = new File(conf.getShiroPath());
+      originalShiro = StringUtils.join(FileUtils.readLines(file, "UTF-8"), "\n");
+      FileUtils.write(file, authShiro, "UTF-8");
+    } catch (IOException e) {
+      LOG.error("Error in AuthenticationIT startUp::", e);
+    }
+    ZeppelinITUtils.restartZeppelin();
+    driver = WebDriverManager.getWebDriver();
+  }
+
+
+  @AfterClass
+  public static void tearDown() {
+    if (!endToEndTestEnabled()) {
+      return;
+    }
+    try {
+      ZeppelinConfiguration conf = ZeppelinConfiguration.create();
+      File file = new File(conf.getShiroPath());
+      FileUtils.write(file, originalShiro, "UTF-8");
+    } catch (IOException e) {
+      LOG.error("Error in AuthenticationIT tearDown::", e);
+    }
+    ZeppelinITUtils.restartZeppelin();
+    driver.quit();
+  }
+
+  private void authenticationUser(String userName, String password) {
+    pollingWait(By.xpath(
+        "//div[contains(@class, 'navbar-collapse')]//li//button[contains(.,'Login')]"),
+        MAX_BROWSER_TIMEOUT_SEC).click();
+    sleep(1000, false);
+    pollingWait(By.xpath("//*[@id='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(userName);
+    pollingWait(By.xpath("//*[@id='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(password);
+    pollingWait(By.xpath("//*[@id='NoteImportCtrl']//button[contains(.,'Login')]"),
+        MAX_BROWSER_TIMEOUT_SEC).click();
+    sleep(1000, false);
+  }
+
+  private void logoutUser(String userName) {
+    sleep(500, false);
+    driver.findElement(By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" +
+        userName + "')]")).click();
+    sleep(500, false);
+    driver.findElement(By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" +
+        userName + "')]//a[@ng-click='logout()']")).click();
+    sleep(5000, false);
+  }
+
+  //  @Test
+  public void testSimpleAuthentication() throws Exception {
+    if (!endToEndTestEnabled()) {
+      return;
+    }
+    try {
+      AuthenticationIT authenticationIT = new AuthenticationIT();
+      authenticationIT.authenticationUser("admin", "password1");
+
+      collector.checkThat("Check is user logged in", true,
+          CoreMatchers.equalTo(driver.findElement(By.partialLinkText("Create new note"))
+              .isDisplayed()));
+
+      authenticationIT.logoutUser("admin");
+    } catch (Exception e) {
+      handleException("Exception in ParagraphActionsIT while testCreateNewButton ", e);
+    }
+  }
+
+  @Test
+  public void testGroupPermission() throws Exception {
+    if (!endToEndTestEnabled()) {
+      return;
+    }
+    try {
+      AuthenticationIT authenticationIT = new AuthenticationIT();
+      authenticationIT.authenticationUser("finance1", "finance1");
+      createNewNote();
+
+      String noteId = driver.getCurrentUrl().substring(driver.getCurrentUrl().lastIndexOf("/") + 1);
+
+      pollingWait(By.xpath("//button[@tooltip='Note permissions']"),
+          MAX_BROWSER_TIMEOUT_SEC).sendKeys(Keys.ENTER);
+      pollingWait(By.xpath("//input[@ng-model='permissions.owners']"), MAX_BROWSER_TIMEOUT_SEC)
+          .sendKeys("finance");
+      pollingWait(By.xpath("//input[@ng-model='permissions.readers']"), MAX_BROWSER_TIMEOUT_SEC)
+          .sendKeys("finance");
+      pollingWait(By.xpath("//input[@ng-model='permissions.writers']"), MAX_BROWSER_TIMEOUT_SEC)
+          .sendKeys("finance");
+      pollingWait(By.xpath("//button[@ng-click='savePermissions()']"), MAX_BROWSER_TIMEOUT_SEC)
+          .sendKeys(Keys.ENTER);
+
+      pollingWait(By.xpath("//div[@class='modal-dialog'][contains(.,'Permissions Saved ')]" +
+              "//div[@class='modal-footer']//button[contains(.,'OK')]"),
+          MAX_BROWSER_TIMEOUT_SEC).click();
+      authenticationIT.logoutUser("finance1");
+
+      authenticationIT.authenticationUser("hr1", "hr1");
+      pollingWait(By.xpath("//*[@id='notebook-names']//a[contains(@href, '" + noteId + "')]"),
+          MAX_BROWSER_TIMEOUT_SEC).click();
+
+      List<WebElement> privilegesModal = driver.findElements(
+          By.xpath("//div[@class='modal-content']//div[@class='bootstrap-dialog-header']" +
+              "//div[contains(.,'Insufficient privileges')]"));
+      collector.checkThat("Check is user has permission to view this notebook", 1,
+          CoreMatchers.equalTo(privilegesModal.size()));
+      driver.findElement(
+          By.xpath("//div[@class='modal-content'][contains(.,'Insufficient privileges')]" +
+              "//div[@class='modal-footer']//button[2]")).click();
+      authenticationIT.logoutUser("hr1");
+
+      authenticationIT.authenticationUser("finance2", "finance2");
+      pollingWait(By.xpath("//*[@id='notebook-names']//a[contains(@href, '" + noteId + "')]"),
+          MAX_BROWSER_TIMEOUT_SEC).click();
+
+      privilegesModal = driver.findElements(
+          By.xpath("//div[@class='modal-content']//div[@class='bootstrap-dialog-header']" +
+              "//div[contains(.,'Insufficient privileges')]"));
+      collector.checkThat("Check is user has permission to view this notebook", 0,
+          CoreMatchers.equalTo(privilegesModal.size()));
+      deleteTestNotebook(driver);
+      authenticationIT.logoutUser("finance2");
+
+
+    } catch (Exception e) {
+      handleException("Exception in ParagraphActionsIT while testGroupPermission ", e);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/24922e10/zeppelin-web/src/app/notebook/notebook-actionBar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html
index fc1932f..28dd84e 100644
--- a/zeppelin-web/src/app/notebook/notebook-actionBar.html
+++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html
@@ -151,21 +151,26 @@ limitations under the License.
     </span>
 
     <div class="pull-right" style="margin-top:15px; margin-right:15px; font-size:15px;">
-      <span style="position:relative; top:3px; margin-right:4px; cursor:pointer"
+      <span>
+        <button type="button"
+            class="btn btn-default btn-xs"
             data-toggle="modal"
             data-target="#shortcutModal"
             tooltip-placement="bottom" tooltip="List of shortcut">
-        <i class="icon-question"></i>
-      </span>
-      <span style="position:relative; top:2px; margin-right:4px; cursor:pointer;"
+          <i class="icon-question"></i>
+        </button>
+        <button type="button"
+            class="btn btn-default btn-xs"
             ng-click="toggleSetting()"
             tooltip-placement="bottom" tooltip="Interpreter binding">
-        <i class="fa fa-cog" ng-style="{color: showSetting ? '#3071A9' : 'black' }"></i>
-      </span>
-      <span style="position:relative; top:2px; margin-right:4px; cursor:pointer;"
+          <i class="fa fa-cog" ng-style="{color: showSetting ? '#3071A9' : 'black' }"></i>
+        </button>
+        <button type="button"
+            class="btn btn-default btn-xs"
             ng-click="togglePermissions()"
             tooltip-placement="bottom" tooltip="Note permissions">
-        <i class="fa fa-lock" ng-style="{color: showPermissions ? '#3071A9' : 'black' }"></i>
+          <i class="fa fa-lock" ng-style="{color: showPermissions ? '#3071A9' : 'black' }"></i>
+        </button>
       </span>
 
       <span class="btn-group">