Browse Source
Merge in DESIGN/design from ~HENRY.WANG/design:release/11.0 to release/11.0 * commit '83aa0647da69837fb1bf688599d99a0a840b2ef2': REPORT-56220 数据连接越权漏洞修复 REPORT-56220 数据连接越权漏洞修复 REPORT-56220 数据连接越权漏洞修复 REPORT-56220 数据连接越权漏洞修复bugfix/11.0
Henry.Wang
3 years ago
7 changed files with 332 additions and 7 deletions
@ -0,0 +1,20 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.report.cell.cellattr.core.group.DSColumn; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
public class DSColumnAuthorityChecker extends ElementAuthorityChecker<DSColumn> { |
||||||
|
|
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthDatasetNames(DSColumn dsColumn, Set<String> authDatasetNames) { |
||||||
|
if (!authDatasetNames.contains(dsColumn.getDSName())) { |
||||||
|
return new HashSet<>(Arrays.asList(dsColumn.getDSName())); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; |
||||||
|
|
||||||
|
import java.lang.reflect.Type; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
|
||||||
|
public abstract class ElementAuthorityChecker<T> { |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @Description 获取越权的数据连接 |
||||||
|
* @param: t 待检查的对象 |
||||||
|
* @param: authConnectionNames 有权限的数据连接名 |
||||||
|
* @return 如果有返回名称,没有返回null |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthConnectionNames(T t, Set<String> authConnectionNames) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @Description 获取越权的服务器数据集 |
||||||
|
* @param: t 待检查的对象 |
||||||
|
* @param: authDatasetNames 有权限的服务器数据集名 |
||||||
|
* @return 如果有返回名称,没有返回null |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthDatasetNames(T t, Set<String> authDatasetNames) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @Description 要检查对象的className |
||||||
|
* @return className |
||||||
|
*/ |
||||||
|
String getCheckClassName() { |
||||||
|
ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) this.getClass().getGenericSuperclass(); |
||||||
|
Type type = parameterizedType.getActualTypeArguments()[0]; |
||||||
|
return type.getTypeName(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.base.Formula; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
public class FormulaAuthorityChecker extends ElementAuthorityChecker<Formula> { |
||||||
|
private static final Pattern FORMULA_PATTERN = Pattern.compile("^=SQL\\(\"(.+?)\","); |
||||||
|
|
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
public Set<String> getNoAuthConnectionNames(Formula formula, Set<String> authConnectionNames) { |
||||||
|
String content = formula.getContent(); |
||||||
|
Matcher matcher = FORMULA_PATTERN.matcher(content); |
||||||
|
if (matcher.find()) { |
||||||
|
if (!authConnectionNames.contains(matcher.group(1))) { |
||||||
|
return new HashSet<>(Arrays.asList(matcher.group(1))); |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,184 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.JTemplate; |
||||||
|
import com.fr.design.mod.ModClassFilter; |
||||||
|
import com.fr.invoke.ClassHelper; |
||||||
|
|
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.rpc.ExceptionHandler; |
||||||
|
import com.fr.rpc.RPCInvokerExceptionInfo; |
||||||
|
import com.fr.stable.Filter; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import com.fr.workspace.server.authority.user.UserAuthority; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
|
||||||
|
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||||
|
|
||||||
|
|
||||||
|
public class JTemplateAuthorityChecker { |
||||||
|
JTemplate<?, ?> jTemplate; |
||||||
|
Set<String> authConnectionNames; |
||||||
|
Set<String> authDatasetNames; |
||||||
|
Map<String, ElementAuthorityChecker> checkerMap = new HashMap<>(); |
||||||
|
Set<String> authFailConnectionNames = new HashSet<>(); |
||||||
|
Set<String> authFailDatasetNames = new HashSet<>(); |
||||||
|
|
||||||
|
|
||||||
|
public JTemplateAuthorityChecker(JTemplate<?, ?> jTemplate) { |
||||||
|
long s = System.currentTimeMillis(); |
||||||
|
this.jTemplate = jTemplate; |
||||||
|
this.initAuthNames(); |
||||||
|
this.initChecker(); |
||||||
|
FineLoggerFactory.getLogger().info("JTemplateAuthorityChecker init time consume:" + (System.currentTimeMillis() - s)); |
||||||
|
} |
||||||
|
|
||||||
|
private void initAuthNames() { |
||||||
|
UserAuthority templateAuthority = WorkContext.getCurrent().get(UserAuthority.class); |
||||||
|
Map<String, Set<String>> authNamesMap = templateAuthority.getAuthServerDataSetAndConnectionNames(); |
||||||
|
if (authNamesMap != null) { |
||||||
|
//有权限的数据连接名称
|
||||||
|
authConnectionNames = authNamesMap.get(UserAuthority.AUTH_CONNECTION_NAMES); |
||||||
|
//有权限的数据集名称(模板数据集和服务器数据集)
|
||||||
|
authDatasetNames = authNamesMap.get(UserAuthority.AUTH_SERVER_DATASET_NAMES); |
||||||
|
Iterator<String> iterator = jTemplate.getTarget().getTableDataNameIterator(); |
||||||
|
while (iterator.hasNext()) { |
||||||
|
String datasetName = iterator.next(); |
||||||
|
authDatasetNames.add(datasetName); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initChecker() { |
||||||
|
registerChecker(new NameDatabaseConnectionAuthorityChecker()); |
||||||
|
registerChecker(new DSColumnAuthorityChecker()); |
||||||
|
registerChecker(new FormulaAuthorityChecker()); |
||||||
|
registerChecker(new NameTableDataAuthorityChecker()); |
||||||
|
} |
||||||
|
|
||||||
|
private void registerChecker(ElementAuthorityChecker checker) { |
||||||
|
checkerMap.put(checker.getCheckClassName(), checker); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public boolean isAuthority() { |
||||||
|
long s = System.currentTimeMillis(); |
||||||
|
//遍历模板对象,根据checkerMap.keySet()把感兴趣的对象找出来
|
||||||
|
Map<String, Collection<Object>> targetObjects = ClassHelper.searchObject(jTemplate.getTarget(), checkerMap.keySet(), ClassFilter.getInstance()); |
||||||
|
|
||||||
|
//找到对应的checker,对对象进行检查
|
||||||
|
for (String name : targetObjects.keySet()) { |
||||||
|
ElementAuthorityChecker checker = checkerMap.get(name); |
||||||
|
for (Object object : targetObjects.get(name)) { |
||||||
|
if (authConnectionNames != null) { |
||||||
|
Set<String> noAuthName = checker.getNoAuthConnectionNames(object, authConnectionNames); |
||||||
|
if (noAuthName != null) { |
||||||
|
authFailConnectionNames.addAll(noAuthName); |
||||||
|
} |
||||||
|
} |
||||||
|
if (authDatasetNames != null) { |
||||||
|
Set<String> noAuthName = checker.getNoAuthDatasetNames(object, authDatasetNames); |
||||||
|
if (noAuthName != null) { |
||||||
|
authFailDatasetNames.addAll(noAuthName); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FineLoggerFactory.getLogger().info("JTemplateAuthorityChecker check time consume:" + (System.currentTimeMillis() - s)); |
||||||
|
return authFailConnectionNames.size() == 0 && authFailDatasetNames.size() == 0; |
||||||
|
} |
||||||
|
|
||||||
|
public void showAuthorityFailPromptDialog() { |
||||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design-Basic_Save_Failure")); |
||||||
|
stringBuffer.append("\n"); |
||||||
|
stringBuffer.append(getPromptInfo(authFailDatasetNames, |
||||||
|
Toolkit.i18nText("Fine-Design_Template_Authority_Check_Server_Dataset_Authority"))); |
||||||
|
stringBuffer.append(getPromptInfo(authFailConnectionNames, |
||||||
|
Toolkit.i18nText("Fine-Design_Template_Authority_Check_Data_Connection_Authority"))); |
||||||
|
FineJOptionPane.showMessageDialog( |
||||||
|
DesignerContext.getDesignerFrame(), |
||||||
|
stringBuffer.toString(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
} |
||||||
|
|
||||||
|
private String getPromptInfo(Set<String> authFailNames, String message) { |
||||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||||
|
if (authFailNames.size() > 0) { |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design_Template_Authority_Check_Current_Operator_Miss")); |
||||||
|
stringBuffer.append(authFailNames.size()); |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design_Report_Ge")); |
||||||
|
stringBuffer.append(message); |
||||||
|
stringBuffer.append("\n"); |
||||||
|
stringBuffer.append(getNoAuthNameSequence(authFailNames)); |
||||||
|
} |
||||||
|
return stringBuffer.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private String getNoAuthNameSequence(Set<String> names) { |
||||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||||
|
int showMaxCount = 3; |
||||||
|
int count = 0; |
||||||
|
for (String name : names) { |
||||||
|
if (count == showMaxCount) { |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design_Template_Authority_Check_Etc")); |
||||||
|
break; |
||||||
|
} |
||||||
|
stringBuffer.append(name); |
||||||
|
if (count != names.size() - 1 && count != showMaxCount - 1) { |
||||||
|
stringBuffer.append(";"); |
||||||
|
} |
||||||
|
count++; |
||||||
|
} |
||||||
|
stringBuffer.append("\n"); |
||||||
|
return stringBuffer.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
static class ClassFilter implements Filter<String> { |
||||||
|
|
||||||
|
private static final Set<String> FILTER_SET = new HashSet<>(); |
||||||
|
private static final Set<String> START_WITH_SET = new HashSet<>(); |
||||||
|
private static final Filter<String> INSTANCE = new ModClassFilter(); |
||||||
|
|
||||||
|
public static Filter<String> getInstance() { |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
static { |
||||||
|
FILTER_SET.add("java.awt.image.BufferedImage"); |
||||||
|
FILTER_SET.add("sun.awt.AppContext"); |
||||||
|
FILTER_SET.add("com.fr.poly.creator.ECBlockCreator"); |
||||||
|
FILTER_SET.add("io.netty.channel.nio.SelectedSelectionKeySet"); |
||||||
|
FILTER_SET.add("com.fr.form.ui.ElementCaseImage"); |
||||||
|
FILTER_SET.add("this$0"); |
||||||
|
START_WITH_SET.add("com.fr.design"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean accept(String s) { |
||||||
|
if (FILTER_SET.contains(s)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
for (String start : START_WITH_SET) { |
||||||
|
if (s.startsWith(start)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,22 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.data.impl.NameDatabaseConnection; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
public class NameDatabaseConnectionAuthorityChecker extends ElementAuthorityChecker<NameDatabaseConnection> { |
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthConnectionNames(NameDatabaseConnection nameDatabaseConnection, Set<String> authConnectionNames) { |
||||||
|
String name = nameDatabaseConnection.getName(); |
||||||
|
if (!authConnectionNames.contains(name)) { |
||||||
|
return new HashSet<>(Arrays.asList(name)); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.data.impl.NameTableData; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
public class NameTableDataAuthorityChecker extends ElementAuthorityChecker<NameTableData> { |
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthDatasetNames(NameTableData nameTableData, Set<String> authDatasetNames) { |
||||||
|
if (!authDatasetNames.contains(nameTableData.getName())) { |
||||||
|
return new HashSet<>(Arrays.asList(nameTableData.getName())); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue