This is an automated email from the ASF dual-hosted git repository.

healchow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git


The following commit(s) were added to refs/heads/master by this push:
     new d3d1a0b196 [INLONG-8290][Manager] Add tenant condition into query by 
SQL interceptor (#8304)
d3d1a0b196 is described below

commit d3d1a0b196494b95790116c6a0227cfa02e09737
Author: vernedeng <[email protected]>
AuthorDate: Mon Jun 26 20:56:10 2023 +0800

    [INLONG-8290][Manager] Add tenant condition into query by SQL interceptor 
(#8304)
    
    * [INLONG-8290][Manager] Support sql interceptor to add tenant into each 
query
    
    * [INLONG-8290][Manager] Optimize multi-tenant code structure
    
    * [INLONG-8290][Manager] Format the code style
    
    ---------
    
    Co-authored-by: healchow <[email protected]>
---
 .../manager/common/consts/InlongConstants.java     |   4 +
 .../manager/common/tenant/MultiTenantQuery.java    |  36 ++++++
 inlong-manager/manager-dao/pom.xml                 |   4 +
 .../manager/dao/config/JDBCSourceConfig.java       |   4 +-
 .../dao/interceptor/MultiTenantInterceptor.java    | 121 +++++++++++++++++++++
 .../dao/interceptor/MultiTenantQueryFilter.java    | 100 +++++++++++++++++
 pom.xml                                            |   6 +
 7 files changed, 274 insertions(+), 1 deletion(-)

diff --git 
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/consts/InlongConstants.java
 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/consts/InlongConstants.java
index 54ef5f816b..3ce5d5e9e8 100644
--- 
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/consts/InlongConstants.java
+++ 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/consts/InlongConstants.java
@@ -50,6 +50,10 @@ public class InlongConstants {
 
     public static final String SEMICOLON = ";";
 
+    public static final String HYPHEN = "-";
+
+    public static final String UNDERSCORE = "_";
+
     public static final String LEFT_BRACKET = "(";
 
     public static final String PERCENT = "%";
diff --git 
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tenant/MultiTenantQuery.java
 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tenant/MultiTenantQuery.java
new file mode 100644
index 0000000000..9996220806
--- /dev/null
+++ 
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tenant/MultiTenantQuery.java
@@ -0,0 +1,36 @@
+/*
+ * 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.inlong.manager.common.tenant;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation indicate that SQL queries from this type or method should
+ * be conditioned by tenant, which is obtained from the login user.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface MultiTenantQuery {
+
+    boolean with() default true;
+}
diff --git a/inlong-manager/manager-dao/pom.xml 
b/inlong-manager/manager-dao/pom.xml
index 6235e16904..ec1e0b198a 100644
--- a/inlong-manager/manager-dao/pom.xml
+++ b/inlong-manager/manager-dao/pom.xml
@@ -89,6 +89,10 @@
             <groupId>com.github.pagehelper</groupId>
             <artifactId>pagehelper-spring-boot-starter</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.jsqlparser</groupId>
+            <artifactId>jsqlparser</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
diff --git 
a/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/config/JDBCSourceConfig.java
 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/config/JDBCSourceConfig.java
index 9d7296a942..263fea8b43 100644
--- 
a/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/config/JDBCSourceConfig.java
+++ 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/config/JDBCSourceConfig.java
@@ -17,6 +17,8 @@
 
 package org.apache.inlong.manager.dao.config;
 
+import org.apache.inlong.manager.dao.interceptor.MultiTenantInterceptor;
+
 import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.mybatis.spring.SqlSessionFactoryBean;
@@ -52,7 +54,7 @@ public class JDBCSourceConfig {
         SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
         bean.setDataSource(dataSource());
         bean.setMapperLocations(new 
PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
-
+        
Objects.requireNonNull(bean.getObject()).getConfiguration().addInterceptor(new 
MultiTenantInterceptor());
         
Objects.requireNonNull(bean.getObject()).getConfiguration().setMapUnderscoreToCamelCase(true);
         return bean.getObject();
     }
diff --git 
a/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/interceptor/MultiTenantInterceptor.java
 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/interceptor/MultiTenantInterceptor.java
new file mode 100644
index 0000000000..00f3f67cb6
--- /dev/null
+++ 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/interceptor/MultiTenantInterceptor.java
@@ -0,0 +1,121 @@
+/*
+ * 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.inlong.manager.dao.interceptor;
+
+import org.apache.inlong.manager.common.consts.InlongConstants;
+import org.apache.inlong.manager.pojo.user.LoginUserUtils;
+import org.apache.inlong.manager.pojo.user.UserInfo;
+
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.parser.CCJSqlParserManager;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.Select;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Invocation;
+import org.apache.ibatis.plugin.Plugin;
+import org.apache.ibatis.plugin.Signature;
+import org.apache.ibatis.reflection.DefaultReflectorFactory;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.SystemMetaObject;
+
+import java.io.StringReader;
+import java.sql.Connection;
+import java.util.Properties;
+
+/**
+ * Interceptor for multi-tenant.
+ */
+@Intercepts({
+        @Signature(type = StatementHandler.class, method = "prepare", args = 
{Connection.class, Integer.class})
+})
+public class MultiTenantInterceptor implements Interceptor {
+
+    private static final String TENANT_CONDITION = "tenant=";
+
+    @Override
+    public Object intercept(Invocation invocation) throws Throwable {
+        StatementHandler statementHandler = (StatementHandler) 
invocation.getTarget();
+        MetaObject metaObject = MetaObject.forObject(statementHandler, 
SystemMetaObject.DEFAULT_OBJECT_FACTORY,
+                SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new 
DefaultReflectorFactory());
+        MappedStatement mappedStatement = (MappedStatement) 
metaObject.getValue("delegate.mappedStatement");
+
+        String fullMethodName = mappedStatement.getId();
+        if 
(!MultiTenantQueryFilter.isMultiTenantQuery(fullMethodName.split(InlongConstants.UNDERSCORE)[0]))
 {
+            return invocation.proceed();
+        }
+
+        BoundSql boundSql = statementHandler.getBoundSql();
+        String sql = boundSql.getSql();
+
+        CCJSqlParserManager parserManager = new CCJSqlParserManager();
+        Select select = (Select) parserManager.parse(new StringReader(sql));
+        PlainSelect plain = (PlainSelect) select.getSelectBody();
+
+        StringBuilder whereSql = new StringBuilder();
+        whereSql.append(TENANT_CONDITION).append(getTenant());
+
+        Expression where = plain.getWhere();
+        if (where == null) {
+            Expression expression = 
CCJSqlParserUtil.parseCondExpression(whereSql.toString());
+            plain.setWhere(expression);
+        } else {
+            if (where.toString().contains(TENANT_CONDITION)) {
+                return invocation.proceed();
+            }
+
+            // else, append the tenant condition
+            whereSql.append(" and ( ").append(where).append(" )");
+            Expression expression = 
CCJSqlParserUtil.parseCondExpression(whereSql.toString());
+            plain.setWhere(expression);
+        }
+        metaObject.setValue("delegate.boundSql.sql", select.toString());
+        return invocation.proceed();
+    }
+
+    private static String getTenant() {
+        UserInfo userInfo = LoginUserUtils.getLoginUser();
+        if (userInfo == null) {
+            throw new IllegalStateException("current login user is null, 
please login first");
+        }
+        String tenant = userInfo.getTenant();
+        if (StringUtils.isBlank(tenant)) {
+            throw new IllegalStateException("get no target tenant of 
userInfo=" + userInfo);
+        }
+        return tenant;
+    }
+
+    @Override
+    public Object plugin(Object target) {
+        if (target instanceof StatementHandler) {
+            return Plugin.wrap(target, this);
+        } else {
+            return target;
+        }
+    }
+
+    @Override
+    public void setProperties(Properties properties) {
+
+    }
+}
diff --git 
a/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/interceptor/MultiTenantQueryFilter.java
 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/interceptor/MultiTenantQueryFilter.java
new file mode 100644
index 0000000000..8cf99587d8
--- /dev/null
+++ 
b/inlong-manager/manager-dao/src/main/java/org/apache/inlong/manager/dao/interceptor/MultiTenantQueryFilter.java
@@ -0,0 +1,100 @@
+/*
+ * 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.inlong.manager.dao.interceptor;
+
+import org.apache.inlong.manager.common.tenant.MultiTenantQuery;
+
+import lombok.extern.slf4j.Slf4j;
+import org.reflections.Reflections;
+import org.reflections.scanners.Scanners;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Filter to check if SQLs from some method should add tenant condition or not.
+ */
+@Slf4j
+@Component
+public class MultiTenantQueryFilter {
+
+    private static final String METHOD_FILTER_PATH = 
"org.apache.inlong.manager.dao.mapper";
+
+    private static final Set<String> METHOD_SET = new HashSet<>();
+
+    /**
+     * Check whether the specified method supports multi-tenant queries.
+     *
+     * @param methodName method name
+     * @return true if supports multi-tenant query, false if not
+     */
+    public static boolean isMultiTenantQuery(String methodName) {
+        return METHOD_SET.contains(methodName);
+    }
+
+    /**
+     * Find all methods that support multi-tenant queries - used 
MultiTenantQuery annotation.
+     */
+    @PostConstruct
+    private void init() {
+        Reflections methodReflections = new Reflections(METHOD_FILTER_PATH, 
Scanners.MethodsAnnotated);
+        // process methods
+        Set<Method> methodSet = 
methodReflections.getMethodsAnnotatedWith(MultiTenantQuery.class);
+        markMethods(methodSet);
+
+        // process classes
+        Reflections reflections = new Reflections(METHOD_FILTER_PATH, 
Scanners.TypesAnnotated);
+        Set<Class<?>> clazzSet = 
reflections.getTypesAnnotatedWith(MultiTenantQuery.class);
+        clazzSet.stream()
+                .filter(Class::isInterface)
+                .forEach(clazz -> {
+                    // Get the JsonTypeDefine annotation
+                    MultiTenantQuery annotation = 
clazz.getAnnotation(MultiTenantQuery.class);
+                    if (annotation == null || !annotation.with()) {
+                        return;
+                    }
+                    List<Method> methods = Arrays.asList(clazz.getMethods());
+                    markMethods(methods);
+                });
+
+        log.debug("success to find all methods that support multi-tenant 
queries, methods={}", METHOD_SET);
+    }
+
+    private static void markMethods(Collection<Method> methods) {
+        methods.forEach(method -> {
+            MultiTenantQuery annotation = 
method.getAnnotation(MultiTenantQuery.class);
+            if (annotation != null && !annotation.with()) {
+                METHOD_SET.remove(getMethodFullName(method));
+            } else {
+                METHOD_SET.add(getMethodFullName(method));
+            }
+        });
+    }
+
+    private static String getMethodFullName(Method method) {
+        return method.getDeclaringClass().getName() + "." + method.getName();
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index 32a9ffc78b..2fa96ff74a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -92,6 +92,7 @@
         <spring.fox.version>3.0.0</spring.fox.version>
         <pagehelper.springboot.version>1.4.2</pagehelper.springboot.version>
         <pagehelper.version>5.3.1</pagehelper.version>
+        <jsqlparser.version>4.6</jsqlparser.version>
 
         <h2.version>2.1.214</h2.version>
         <h2.mysql.version>2.0.0</h2.mysql.version>
@@ -636,6 +637,11 @@
                 <artifactId>pagehelper-spring-boot-starter</artifactId>
                 <version>${pagehelper.springboot.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.github.jsqlparser</groupId>
+                <artifactId>jsqlparser</artifactId>
+                <version>${jsqlparser.version}</version>
+            </dependency>
 
             <!--netty dependency-->
             <dependency>

Reply via email to