Skip to content

Commit 77287ab

Browse files
committed
fix: get user list for permissions using JdbcRealm
1 parent 9fe7f82 commit 77287ab

File tree

3 files changed

+75
-11
lines changed

3 files changed

+75
-11
lines changed

zeppelin-server/pom.xml

+8
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<selenium.java.version>2.48.2</selenium.java.version>
4646
<xml.apis.version>1.4.01</xml.apis.version>
4747
<hamcrest.version>2.2</hamcrest.version>
48+
<h2.version>2.3.232</h2.version>
4849
</properties>
4950

5051
<dependencies>
@@ -368,6 +369,13 @@
368369
<scope>test</scope>
369370
</dependency>
370371

372+
<dependency>
373+
<groupId>com.h2database</groupId>
374+
<artifactId>h2</artifactId>
375+
<version>${h2.version}</version>
376+
<scope>test</scope>
377+
</dependency>
378+
371379
<dependency>
372380
<groupId>org.bitbucket.cowwoc</groupId>
373381
<artifactId>diff-match-patch</artifactId>

zeppelin-server/src/main/java/org/apache/zeppelin/service/ShiroAuthenticationService.java

+34-11
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@
2626
import java.util.List;
2727
import java.util.Map;
2828
import java.util.Set;
29-
import jakarta.inject.Inject;
29+
import java.util.regex.Pattern;
30+
3031
import javax.naming.NamingEnumeration;
3132
import javax.naming.NamingException;
3233
import javax.naming.directory.Attributes;
3334
import javax.naming.directory.SearchControls;
3435
import javax.naming.directory.SearchResult;
3536
import javax.naming.ldap.LdapContext;
3637
import javax.sql.DataSource;
38+
39+
import jakarta.inject.Inject;
3740
import org.apache.commons.lang3.StringUtils;
3841
import org.apache.commons.lang3.reflect.FieldUtils;
3942
import org.apache.shiro.UnavailableSecurityManagerException;
@@ -68,6 +71,12 @@ public class ShiroAuthenticationService implements AuthenticationService {
6871
private static final String ACTIVE_DIRECTORY_GROUP_REALM = "org.apache.zeppelin.realm.ActiveDirectoryGroupRealm";
6972
private static final String JDBC_REALM = "org.apache.shiro.realm.jdbc.JdbcRealm";
7073

74+
private static final Pattern VALID_SQL_NAME_IDENTIFIER_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+$");
75+
76+
private static boolean isValidSqlIdentifier(String name) {
77+
return name != null && VALID_SQL_NAME_IDENTIFIER_PATTERN.matcher(name).matches();
78+
}
79+
7180
private final ZeppelinConfiguration zConf;
7281

7382
@Inject
@@ -174,7 +183,7 @@ public List<String> getMatchedUsers(String searchText, int numUsersToFetch) {
174183
usersList.addAll(
175184
getUserList((ActiveDirectoryGroupRealm) realm, searchText, numUsersToFetch));
176185
} else if (JDBC_REALM.equals(realClassName)) {
177-
usersList.addAll(getUserList((JdbcRealm) realm));
186+
usersList.addAll(getUserList((JdbcRealm) realm, searchText, numUsersToFetch));
178187
}
179188
}
180189
}
@@ -401,7 +410,7 @@ private List<String> getUserList(
401410
}
402411

403412
/** Function to extract users from JDBCs. */
404-
private List<String> getUserList(JdbcRealm obj) {
413+
private List<String> getUserList(JdbcRealm obj, String searchText, int numUsersToFetch) {
405414
List<String> userlist = new ArrayList<>();
406415
Connection con = null;
407416
PreparedStatement ps = null;
@@ -415,26 +424,39 @@ private List<String> getUserList(JdbcRealm obj) {
415424
try {
416425
dataSource = (DataSource) FieldUtils.readField(obj, "dataSource", true);
417426
authQuery = (String) FieldUtils.readField(obj, "authenticationQuery", true);
418-
LOGGER.info(authQuery);
427+
LOGGER.debug("authenticationQuery={}", authQuery);
419428
String authQueryLowerCase = authQuery.toLowerCase();
420429
retval = authQueryLowerCase.split("from", 2);
421430
if (retval.length >= 2) {
422431
retval = retval[1].split("with|where", 2);
423-
tablename = retval[0];
432+
tablename = retval[0].trim();
424433
retval = retval[1].split("where", 2);
425434
if (retval.length >= 2) {
426435
retval = retval[1].split("=", 2);
427436
} else {
428437
retval = retval[0].split("=", 2);
429438
}
430-
username = retval[0];
439+
username = retval[0].trim();
431440
}
432441

433442
if (StringUtils.isBlank(username) || StringUtils.isBlank(tablename)) {
434443
return userlist;
435444
}
445+
if (!isValidSqlIdentifier(username)) {
446+
throw new IllegalArgumentException(
447+
"Invalid column name in authenticationQuery to build userlist query: "
448+
+ authQuery + ", allowed pattern: " + VALID_SQL_NAME_IDENTIFIER_PATTERN
449+
+ ", name identifier: [" + username + "]");
450+
}
451+
if (!isValidSqlIdentifier(tablename)) {
452+
throw new IllegalArgumentException(
453+
"Invalid table name in authenticationQuery to build userlist query: "
454+
+ authQuery + ", allowed pattern: " + VALID_SQL_NAME_IDENTIFIER_PATTERN
455+
+ ", name identifier: [" + tablename + "]");
456+
}
436457

437-
userquery = "SELECT ? FROM ?";
458+
userquery = String.format("SELECT %s FROM %s WHERE %s LIKE ?", username, tablename, username);
459+
LOGGER.info("Built query for user list. userquery={}", userquery);
438460
} catch (IllegalAccessException e) {
439461
LOGGER.error("Error while accessing dataSource for JDBC Realm", e);
440462
return new ArrayList<>();
@@ -443,11 +465,12 @@ private List<String> getUserList(JdbcRealm obj) {
443465
try {
444466
con = dataSource.getConnection();
445467
ps = con.prepareStatement(userquery);
446-
ps.setString(1, username);
447-
ps.setString(2, tablename);
468+
ps.setString(1, "%" + searchText + "%");
448469
rs = ps.executeQuery();
449-
while (rs.next()) {
450-
userlist.add(rs.getString(1).trim());
470+
int count = 0;
471+
while (rs.next() && count < numUsersToFetch) {
472+
userlist.add(rs.getString(1).trim());
473+
count++;
451474
}
452475
} catch (Exception e) {
453476
LOGGER.error("Error retrieving User list from JDBC Realm", e);

zeppelin-server/src/test/java/org/apache/zeppelin/service/ShiroAuthenticationServiceTest.java

+33
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,22 @@
2323

2424
import java.io.IOException;
2525
import java.security.Principal;
26+
import java.sql.Connection;
27+
import java.sql.Statement;
2628
import java.util.HashSet;
29+
import java.util.List;
2730
import java.util.Set;
2831

2932
import org.apache.commons.lang3.StringUtils;
3033
import org.apache.shiro.mgt.DefaultSecurityManager;
34+
import org.apache.shiro.realm.jdbc.JdbcRealm;
3135
import org.apache.shiro.subject.Subject;
3236
import org.apache.shiro.util.LifecycleUtils;
3337
import org.apache.shiro.util.ThreadContext;
3438
import org.apache.zeppelin.conf.ZeppelinConfiguration;
3539
import org.apache.zeppelin.realm.jwt.KnoxJwtRealm;
3640
import org.apache.zeppelin.service.shiro.AbstractShiroTest;
41+
import org.h2.jdbcx.JdbcDataSource;
3742
import org.junit.jupiter.api.AfterEach;
3843
import org.junit.jupiter.api.BeforeEach;
3944
import org.junit.jupiter.api.Test;
@@ -53,6 +58,34 @@ void setup() throws Exception {
5358
shiroSecurityService = new ShiroAuthenticationService(zConf);
5459
}
5560

61+
@Test
62+
void testGetMatchedUsersWithJdbcRealm() throws Exception {
63+
64+
// given in-memory jdbcRealm with some users
65+
JdbcRealm realm = new JdbcRealm();
66+
JdbcDataSource dataSource = new JdbcDataSource();
67+
dataSource.setURL("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
68+
dataSource.setUser("sa");
69+
realm.setDataSource(dataSource);
70+
71+
LifecycleUtils.init(realm);
72+
DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);
73+
ThreadContext.bind(securityManager);
74+
75+
try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement()) {
76+
stmt.execute("CREATE TABLE users (username VARCHAR PRIMARY KEY, password VARCHAR)");
77+
stmt.execute("INSERT INTO users VALUES ('admin', '')");
78+
stmt.execute("INSERT INTO users VALUES ('test', '')");
79+
}
80+
81+
// when
82+
List<String> users = shiroSecurityService.getMatchedUsers("adm", 10);
83+
84+
// then
85+
assertEquals(1, users.size());
86+
assertEquals("admin", users.get(0));
87+
}
88+
5689
@Test
5790
void canGetPrincipalName() {
5891
String expectedName = "java.security.Principal.getName()";

0 commit comments

Comments
 (0)