GUACAMOLE-220: Dynamically detect whether the MariaDB / MySQL server supports recursive CTEs.

This commit is contained in:
Michael Jumper
2018-09-08 18:11:36 -07:00
parent 204b6a4b24
commit dec7b3c340
8 changed files with 300 additions and 43 deletions

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.jdbc;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
import org.apache.ibatis.session.SqlSession;
/**
* A JDBC-specific implementation of Environment that defines generic properties
@@ -143,9 +144,12 @@ public abstract class JDBCEnvironment extends LocalEnvironment {
* not supported, queries that are intended to be recursive may need to be
* invoked multiple times to retrieve the same data.
*
* @param session
* The SqlSession provided by MyBatis for the current transaction.
*
* @return
* true if the database supports recursive queries, false otherwise.
*/
public abstract boolean isRecursiveQuerySupported();
public abstract boolean isRecursiveQuerySupported(SqlSession session);
}

View File

@@ -60,12 +60,21 @@ public interface EntityMapper {
* The identifiers of any known effective groups that should be taken
* into account, such as those defined externally to the database.
*
* @param recursive
* Whether the query should leverage database engine features to return
* absolutely all effective groups, including those inherited through
* group membership. If false, this query will return only one level of
* depth and may need to be executed multiple times. If it is known
* that the database engine in question will always support (or always
* not support) recursive queries, this parameter may be ignored.
*
* @return
* The set of identifiers of all groups that the given entity is a
* member of, including those where membership is inherited through
* membership in other groups.
*/
Set<String> selectEffectiveGroupIdentifiers(@Param("entity") EntityModel entity,
@Param("effectiveGroups") Collection<String> effectiveGroups);
@Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("recursive") boolean recursive);
}

View File

@@ -23,6 +23,8 @@ import com.google.inject.Inject;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.guice.transactional.Transactional;
/**
* Service which provides convenience methods for creating, retrieving, and
@@ -42,6 +44,12 @@ public class EntityService {
@Inject
private EntityMapper entityMapper;
/**
* The current SQL session used by MyBatis.
*/
@Inject
private SqlSession sqlSession;
/**
* Returns the set of all group identifiers of which the given entity is a
* member, taking into account the given collection of known group
@@ -64,20 +72,22 @@ public class EntityService {
* member of, including those where membership is inherited through
* membership in other groups.
*/
@Transactional
public Set<String> retrieveEffectiveGroups(ModeledPermissions<? extends EntityModel> entity,
Collection<String> effectiveGroups) {
// Retrieve the effective user groups of the given entity, recursively if possible
Set<String> identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), effectiveGroups);
boolean recursive = environment.isRecursiveQuerySupported(sqlSession);
Set<String> identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), effectiveGroups, recursive);
// If the set of user groups retrieved was not produced recursively,
// manually repeat the query to expand the set until all effective
// groups have been found
if (!environment.isRecursiveQuerySupported() && !identifiers.isEmpty()) {
if (!recursive && !identifiers.isEmpty()) {
Set<String> previousIdentifiers;
do {
previousIdentifiers = identifiers;
identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), previousIdentifiers);
identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), previousIdentifiers, false);
} while (identifiers.size() > previousIdentifiers.size());
}