From 179ea7316da4f5ac6b241d3cf985ac6c93a6ca00 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 01:05:10 +0530 Subject: [PATCH 1/5] enable search for Active Directory --- .../org/apache/zeppelin/rest/GetUserList.java | 20 +++++-- .../apache/zeppelin/rest/SecurityRestApi.java | 7 ++- .../server/ActiveDirectoryGroupRealm.java | 54 +++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java index c603fe977f6..aabac24cb3c 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java @@ -19,10 +19,12 @@ import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.shiro.realm.jdbc.JdbcRealm; +import org.apache.shiro.realm.ldap.AbstractLdapRealm; import org.apache.shiro.realm.ldap.JndiLdapContextFactory; import org.apache.shiro.realm.ldap.JndiLdapRealm; import org.apache.shiro.realm.text.IniRealm; import org.apache.shiro.util.JdbcUtils; +import org.apache.zeppelin.server.ActiveDirectoryGroupRealm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,7 +68,7 @@ public List getUserList(IniRealm r) { /** * function to extract users from LDAP */ - public List getUserList(JndiLdapRealm r) { + public List getUserList(JndiLdapRealm r, String searchText) { List userList = new ArrayList<>(); String userDnTemplate = r.getUserDnTemplate(); String userDn[] = userDnTemplate.split(",", 2); @@ -79,12 +81,13 @@ public List getUserList(JndiLdapRealm r) { constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); String[] attrIDs = {userDnPrefix}; constraints.setReturningAttributes(attrIDs); - NamingEnumeration result = ctx.search(userDnSuffix, "(objectclass=*)", constraints); + NamingEnumeration result = ctx.search(userDnSuffix, "(" + userDnPrefix + "=*" + searchText + + "*)", constraints); while (result.hasMore()) { Attributes attrs = ((SearchResult) result.next()).getAttributes(); if (attrs.get(userDnPrefix) != null) { String currentUser = attrs.get(userDnPrefix).toString(); - userList.add(currentUser.split(":")[1]); + userList.add(currentUser.split(":")[1].trim()); } } } catch (Exception e) { @@ -93,6 +96,17 @@ public List getUserList(JndiLdapRealm r) { return userList; } + public List getUserList(ActiveDirectoryGroupRealm r, String searchText) { + List userList = new ArrayList<>(); + try { + LdapContext ctx = r.getLdapContextFactory().getSystemLdapContext(); + userList = r.searchForUserName(searchText, ctx); + } catch (Exception e) { + LOG.error("Error retrieving User list from ActiveDirectory Realm", e); + } + return userList; + } + /** * function to extract users from JDBCs */ 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 11c8f96ccb7..0ae4203786b 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 @@ -20,10 +20,12 @@ import org.apache.shiro.realm.Realm; import org.apache.shiro.realm.jdbc.JdbcRealm; +import org.apache.shiro.realm.ldap.AbstractLdapRealm; import org.apache.shiro.realm.ldap.JndiLdapRealm; import org.apache.shiro.realm.text.IniRealm; import org.apache.zeppelin.annotation.ZeppelinApi; import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.server.ActiveDirectoryGroupRealm; import org.apache.zeppelin.server.JsonResponse; import org.apache.zeppelin.ticket.TicketContainer; import org.apache.zeppelin.utils.SecurityUtils; @@ -105,7 +107,10 @@ public Response getUserList(@PathParam("searchText") String searchText) { if (name.equals("iniRealm")) { usersList.addAll(getUserListObj.getUserList((IniRealm) realm)); } else if (name.equals("ldapRealm")) { - usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm)); + usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm, searchText)); + } else if (name.equals("activeDirectoryRealm")) { + usersList.addAll(getUserListObj.getUserList((ActiveDirectoryGroupRealm) realm, + searchText)); } else if (name.equals("jdbcRealm")) { usersList.addAll(getUserListObj.getUserList((JdbcRealm) realm)); } 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 index fc3ccc871d6..387e0ec90a1 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java @@ -24,6 +24,7 @@ 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.DefaultLdapContextFactory; import org.apache.shiro.realm.ldap.LdapContextFactory; import org.apache.shiro.realm.ldap.LdapUtils; import org.apache.shiro.subject.PrincipalCollection; @@ -77,6 +78,25 @@ public void setGroupRolesMap(Map groupRolesMap) { | M E T H O D S | ============================================*/ + LdapContextFactory ldapContextFactory; + + public LdapContextFactory getLdapContextFactory() { + if (this.ldapContextFactory == null) { + if (log.isDebugEnabled()) { + log.debug("No LdapContextFactory specified - creating a default instance."); + } + + DefaultLdapContextFactory defaultFactory = new DefaultLdapContextFactory(); + defaultFactory.setPrincipalSuffix(this.principalSuffix); + defaultFactory.setSearchBase(this.searchBase); + defaultFactory.setUrl(this.url); + defaultFactory.setSystemUsername(this.systemUsername); + defaultFactory.setSystemPassword(this.systemPassword); + this.ldapContextFactory = defaultFactory; + } + + return this.ldapContextFactory; + } /** * Builds an {@link AuthenticationInfo} object by querying the active directory LDAP context for @@ -159,6 +179,40 @@ protected AuthorizationInfo buildAuthorizationInfo(Set roleNames) { return new SimpleAuthorizationInfo(roleNames); } + public List searchForUserName(String nameILIke, LdapContext ldapContext) throws + NamingException { + List userNameList = new ArrayList<>(); + + SearchControls searchCtls = new SearchControls(); + searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + String searchFilter = "(&(objectClass=*)(userPrincipalName=*" + nameILIke + "*))"; + Object[] searchArguments = new Object[]{nameILIke}; + + NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, + searchCtls); + + while (answer.hasMoreElements()) { + SearchResult sr = (SearchResult) answer.next(); + + if (log.isDebugEnabled()) { + log.debug("Retrieving userprincipalname 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().toLowerCase().equals("cn")) { + userNameList.addAll(LdapUtils.getAllAttributeValues(attr)); + } + } + } + } + return userNameList; + } + private Set getRoleNamesForUser(String username, LdapContext ldapContext) throws NamingException { Set roleNames = new LinkedHashSet<>(); From d5e5d96af9c10030bc8bf891303859759cd83242 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 01:05:33 +0530 Subject: [PATCH 2/5] update search preference --- .../org/apache/zeppelin/rest/SecurityRestApi.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 0ae4203786b..b8bfc9f622b 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 @@ -95,7 +95,7 @@ public Response ticket() { */ @GET @Path("userlist/{searchText}") - public Response getUserList(@PathParam("searchText") String searchText) { + public Response getUserList(@PathParam("searchText") final String searchText) { List usersList = new ArrayList<>(); try { @@ -121,6 +121,17 @@ public Response getUserList(@PathParam("searchText") String searchText) { } List autoSuggestList = new ArrayList<>(); Collections.sort(usersList); + Collections.sort(usersList, new Comparator() { + @Override + public int compare(String o1, String o2) { + if (o1.matches(searchText + "(.*)") && o2.matches(searchText + "(.*)")) { + return 0; + } else if (o1.matches(searchText + "(.*)")) { + return -1; + } + return 0; + } + }); int maxLength = 0; for (int i = 0; i < usersList.size(); i++) { String userLowerCase = usersList.get(i).toLowerCase(); From 57de67fc49de6e74c22d4b77abe7d08a4eb98260 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 01:35:22 +0530 Subject: [PATCH 3/5] implement debounce --- .../src/app/notebook/notebook.controller.js | 37 +++++++++++++------ zeppelin-web/src/app/notebook/notebook.html | 6 +-- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 944402a2f3a..4c5993592aa 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -816,15 +816,16 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', function convertToArray(role) { - if (role === 'owners') { + if (!$scope.permissions) { + return; + } else if (role === 'owners' && typeof $scope.permissions.owners === 'string') { searchText = $scope.permissions.owners.split(','); - } - else if (role === 'readers') { + } else if (role === 'readers' && typeof $scope.permissions.readers === 'string') { searchText = $scope.permissions.readers.split(','); - } - else if (role === 'writers') { + } else if (role === 'writers' && typeof $scope.permissions.writers === 'string') { searchText = $scope.permissions.writers.split(','); } + for (var i = 0; i < searchText.length; i++) { searchText[i] = searchText[i].trim(); } @@ -877,6 +878,22 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', updatePreviousList(); }; + $scope.$watch("permissions.owners", _.debounce(function(readers) { + $scope.$apply(function() { + $scope.search('owners'); + }) + }, 350)); + $scope.$watch("permissions.readers", _.debounce(function(readers) { + $scope.$apply(function() { + $scope.search('readers'); + }) + }, 350)); + $scope.$watch("permissions.writers", _.debounce(function(readers) { + $scope.$apply(function() { + $scope.search('writers'); + }) + }, 350)); + // function to find suggestion list on change $scope.search = function(role) { angular.element('.userlist').show(); @@ -886,12 +903,10 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', $scope.selectIndex = -1; $scope.suggestions = []; selectedUser = searchText[selectedUserIndex]; - if(selectedUser !== ''){ - getSuggestions(selectedUser); - } - else - { - $scope.suggestions = []; + if (selectedUser !== '') { + getSuggestions(selectedUser); + } else { + $scope.suggestions = []; } }; diff --git a/zeppelin-web/src/app/notebook/notebook.html b/zeppelin-web/src/app/notebook/notebook.html index de8cbdfe36f..9b60dd7319e 100644 --- a/zeppelin-web/src/app/notebook/notebook.html +++ b/zeppelin-web/src/app/notebook/notebook.html @@ -72,7 +72,7 @@

Note Permissions (Only note owners can change)

data-ng-model="permissions">

Owners Owners can change permissions,read and write the note.

@@ -87,7 +87,7 @@

Note Permissions (Only note owners can change)

Readers Readers can only read the note.

@@ -101,7 +101,7 @@

Note Permissions (Only note owners can change)

Writers Writers can read and write the note.

From a3d5b5c571fd56739028c9ace7926ef54507135e Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 01:42:13 +0530 Subject: [PATCH 4/5] trim and then add user --- .../main/java/org/apache/zeppelin/rest/GetUserList.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java index aabac24cb3c..b322561af66 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java @@ -17,9 +17,9 @@ package org.apache.zeppelin.rest; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.shiro.realm.jdbc.JdbcRealm; -import org.apache.shiro.realm.ldap.AbstractLdapRealm; import org.apache.shiro.realm.ldap.JndiLdapContextFactory; import org.apache.shiro.realm.ldap.JndiLdapRealm; import org.apache.shiro.realm.text.IniRealm; @@ -60,7 +60,7 @@ public List getUserList(IniRealm r) { Iterator it = getIniUser.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); - userList.add(pair.getKey().toString()); + userList.add(pair.getKey().toString().trim()); } return userList; } @@ -137,7 +137,7 @@ public List getUserList(JdbcRealm obj) { username = retval[0]; } - if (username.equals("") || tablename.equals("")){ + if (StringUtils.isBlank(username) || StringUtils.isBlank(tablename)) { return userlist; } @@ -153,7 +153,7 @@ public List getUserList(JdbcRealm obj) { ps = con.prepareStatement(userquery); rs = ps.executeQuery(); while (rs.next()) { - userlist.add(rs.getString(1)); + userlist.add(rs.getString(1).trim()); } } catch (Exception e) { LOG.error("Error retrieving User list from JDBC Realm", e); From 0de7a3dcd7e617579858620a668f901fae83c0db Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 19:53:49 +0530 Subject: [PATCH 5/5] rename variable and CI fixes --- .../zeppelin/server/ActiveDirectoryGroupRealm.java | 6 +++--- zeppelin-web/src/app/notebook/notebook.controller.js | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) 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 index 387e0ec90a1..cc868d7a1b4 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java @@ -179,15 +179,15 @@ protected AuthorizationInfo buildAuthorizationInfo(Set roleNames) { return new SimpleAuthorizationInfo(roleNames); } - public List searchForUserName(String nameILIke, LdapContext ldapContext) throws + public List searchForUserName(String containString, LdapContext ldapContext) throws NamingException { List userNameList = new ArrayList<>(); SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); - String searchFilter = "(&(objectClass=*)(userPrincipalName=*" + nameILIke + "*))"; - Object[] searchArguments = new Object[]{nameILIke}; + String searchFilter = "(&(objectClass=*)(userPrincipalName=*" + containString + "*))"; + Object[] searchArguments = new Object[]{containString}; NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, searchCtls); diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 4c5993592aa..4e791206972 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -878,20 +878,20 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', updatePreviousList(); }; - $scope.$watch("permissions.owners", _.debounce(function(readers) { + $scope.$watch('permissions.owners', _.debounce(function(readers) { $scope.$apply(function() { $scope.search('owners'); - }) + }); }, 350)); - $scope.$watch("permissions.readers", _.debounce(function(readers) { + $scope.$watch('permissions.readers', _.debounce(function(readers) { $scope.$apply(function() { $scope.search('readers'); - }) + }); }, 350)); - $scope.$watch("permissions.writers", _.debounce(function(readers) { + $scope.$watch('permissions.writers', _.debounce(function(readers) { $scope.$apply(function() { $scope.search('writers'); - }) + }); }, 350)); // function to find suggestion list on change