diff --git a/dinky-admin/pom.xml b/dinky-admin/pom.xml index d0314e32fb..1b55d54c95 100644 --- a/dinky-admin/pom.xml +++ b/dinky-admin/pom.xml @@ -37,6 +37,10 @@ + + org.jasypt + jasypt + com.amazonaws aws-java-sdk-s3 diff --git a/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java b/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java index 17ec63e639..aff6913ade 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java @@ -96,7 +96,14 @@ public Result saveOrUpdate(@RequestBody DataBase database) { @Log(title = "DataBase Get All", businessType = BusinessType.QUERY) @ApiOperation("DataBase Get All") public ProTableResult listDataBases(@RequestBody JsonNode para) { - return databaseService.selectForProTable(para); + final ProTableResult result = databaseService.selectForProTable(para); + // 密码不返回 + if (result != null && result.getData() != null) { + for (DataBase data : result.getData()) { + data.setPassword(null); + } + } + return result; } /** diff --git a/dinky-admin/src/main/java/org/dinky/controller/FragmentVariableController.java b/dinky-admin/src/main/java/org/dinky/controller/FragmentVariableController.java index 41990d71ac..642a9743ec 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/FragmentVariableController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/FragmentVariableController.java @@ -26,6 +26,7 @@ import org.dinky.data.result.ProTableResult; import org.dinky.data.result.Result; import org.dinky.service.FragmentVariableService; +import org.dinky.utils.FragmentVariableUtils; import java.util.ArrayList; import java.util.List; @@ -81,7 +82,17 @@ public Result saveOrUpdate(@RequestBody FragmentVariable fragmentVariable) @Log(title = "FragmentVariable List", businessType = BusinessType.QUERY) @ApiOperation("FragmentVariable List") public ProTableResult listFragmentVariable(@RequestBody JsonNode para) { - return fragmentVariableService.selectForProTable(para); + final ProTableResult result = + fragmentVariableService.selectForProTable(para); + // 敏感值不返回 + if (result != null && result.getData() != null) { + for (FragmentVariable variable : result.getData()) { + if (FragmentVariableUtils.isSensitive(variable.getName())) { + variable.setFragmentValue(null); + } + } + } + return result; } /** diff --git a/dinky-admin/src/main/java/org/dinky/crypto/CryptoComponent.java b/dinky-admin/src/main/java/org/dinky/crypto/CryptoComponent.java new file mode 100644 index 0000000000..9f23fb96b6 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/crypto/CryptoComponent.java @@ -0,0 +1,57 @@ +/* + * + * 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.dinky.crypto; + +import org.jasypt.util.text.AES256TextEncryptor; +import org.springframework.stereotype.Component; + +@Component +public class CryptoComponent { + + private final boolean enabled; + private final AES256TextEncryptor textEncryptor; + + public CryptoComponent(CryptoProperties cryptoProperties) { + this.enabled = cryptoProperties.getEnabled(); + this.textEncryptor = new AES256TextEncryptor(); + if (cryptoProperties.getEncryptionPassword() != null) { + this.textEncryptor.setPassword(cryptoProperties.getEncryptionPassword()); + } + } + + public String encryptText(String result) { + if (!enabled) { + return result; + } + + return textEncryptor.encrypt(result); + } + + public String decryptText(String result) { + if (!enabled) { + return result; + } + + if (result == null) { + return null; + } + return textEncryptor.decrypt(result); + } +} diff --git a/dinky-admin/src/main/java/org/dinky/crypto/CryptoProperties.java b/dinky-admin/src/main/java/org/dinky/crypto/CryptoProperties.java new file mode 100644 index 0000000000..d4b0222949 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/crypto/CryptoProperties.java @@ -0,0 +1,37 @@ +/* + * + * 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.dinky.crypto; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@ConfigurationProperties(prefix = "crypto") +@Component +public class CryptoProperties { + /** 是否启用加解密配置 */ + private Boolean enabled = false; + /** 加密密钥 */ + private String encryptionPassword; +} diff --git a/dinky-admin/src/main/java/org/dinky/data/model/DataBase.java b/dinky-admin/src/main/java/org/dinky/data/model/DataBase.java index 280224e7b3..88e0a34b78 100644 --- a/dinky-admin/src/main/java/org/dinky/data/model/DataBase.java +++ b/dinky-admin/src/main/java/org/dinky/data/model/DataBase.java @@ -20,10 +20,12 @@ package org.dinky.data.model; import org.dinky.metadata.driver.DriverConfig; +import org.dinky.mybatis.crypto.CryptoTypeHandler; import org.dinky.mybatis.model.SuperEntity; import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -36,7 +38,7 @@ */ @Data @EqualsAndHashCode(callSuper = false) -@TableName("dinky_database") +@TableName(value = "dinky_database", autoResultMap = true) public class DataBase extends SuperEntity { private static final long serialVersionUID = -5002272138861566408L; @@ -47,6 +49,8 @@ public class DataBase extends SuperEntity { private String type; private String url; private String username; + + @TableField(typeHandler = CryptoTypeHandler.class) private String password; private String note; diff --git a/dinky-admin/src/main/java/org/dinky/mybatis/crypto/CryptoTypeHandler.java b/dinky-admin/src/main/java/org/dinky/mybatis/crypto/CryptoTypeHandler.java new file mode 100644 index 0000000000..7f27fe06ab --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/mybatis/crypto/CryptoTypeHandler.java @@ -0,0 +1,69 @@ +/* + * + * 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.dinky.mybatis.crypto; + +import org.dinky.context.SpringContextUtils; +import org.dinky.crypto.CryptoComponent; + +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class CryptoTypeHandler extends BaseTypeHandler { + + private final CryptoComponent cryptoComponent; + + public CryptoTypeHandler() { + cryptoComponent = SpringContextUtils.getBeanByClass(CryptoComponent.class); + } + + @Override + public void setNonNullParameter( + PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { + ps.setString(i, encryptText(parameter)); + } + + @Override + public String getNullableResult(ResultSet rs, String columnName) throws SQLException { + return decryptText(rs.getString(columnName)); + } + + @Override + public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return decryptText(rs.getString(columnIndex)); + } + + @Override + public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return decryptText(cs.getString(columnIndex)); + } + + private String encryptText(String result) { + return cryptoComponent.encryptText(result); + } + + private String decryptText(String result) { + return cryptoComponent.decryptText(result); + } +} diff --git a/dinky-admin/src/main/java/org/dinky/mybatis/crypto/MybatisPlusCustomizer.java b/dinky-admin/src/main/java/org/dinky/mybatis/crypto/MybatisPlusCustomizer.java new file mode 100644 index 0000000000..760a009426 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/mybatis/crypto/MybatisPlusCustomizer.java @@ -0,0 +1,109 @@ +/* + * + * 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.dinky.mybatis.crypto; + +import org.apache.ibatis.builder.MapperBuilderAssistant; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ResultMap; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.autoconfigure.SqlSessionFactoryBeanCustomizer; +import com.baomidou.mybatisplus.core.MybatisConfiguration; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; + +@Component +public class MybatisPlusCustomizer implements SqlSessionFactoryBeanCustomizer { + + @Override + public void customize(MybatisSqlSessionFactoryBean factoryBean) { + factoryBean.setConfiguration( + new MybatisConfiguration() { + @Override + public void addMappedStatement(MappedStatement ms) { + final String ns = ms.getId().replaceAll("(.*Mapper)\\.\\w+$", "$1"); + final MapperBuilderAssistant builderAssistant = + new MapperBuilderAssistant(ms.getConfiguration(), ""); + builderAssistant.setCurrentNamespace(ns); + boolean[] changes = new boolean[] {false}; + final List collect = + ms.getResultMaps().stream() + .map( + m -> { + // 仅ResultType为Table且包含TypeHandler时,替换ResultMap为MybatisPlus生成的 + TableName table = + m.getType() + .getAnnotation(TableName.class); + if (table != null) { + TableInfo tableInfo = + TableInfoHelper.getTableInfo( + m.getType()); + if (tableInfo == null) { + tableInfo = + TableInfoHelper.initTableInfo( + builderAssistant, + m.getType()); + } + if (tableInfo != null + && tableInfo.getResultMap() != null + && tableInfo.getFieldList().stream() + .anyMatch( + f -> + f + .getTypeHandler() + != null)) { + ResultMap resultMap = + ms.getConfiguration() + .getResultMap( + tableInfo + .getResultMap()); + if (resultMap != null + && resultMap != m) { + changes[0] = true; + return resultMap; + } + } + } + return m; + }) + .collect(Collectors.toList()); + if (changes[0]) { + final Field field = + ReflectionUtils.findField(ms.getClass(), "resultMaps"); + if (field != null) { + ReflectionUtils.makeAccessible(field); + ReflectionUtils.setField( + field, ms, Collections.unmodifiableList(collect)); + } + } + super.addMappedStatement(ms); + } + }); + } +} diff --git a/dinky-admin/src/main/java/org/dinky/service/impl/FragmentVariableServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/impl/FragmentVariableServiceImpl.java index 8351e5f187..aa830d4084 100644 --- a/dinky-admin/src/main/java/org/dinky/service/impl/FragmentVariableServiceImpl.java +++ b/dinky-admin/src/main/java/org/dinky/service/impl/FragmentVariableServiceImpl.java @@ -19,18 +19,25 @@ package org.dinky.service.impl; +import org.dinky.crypto.CryptoComponent; import org.dinky.data.model.FragmentVariable; +import org.dinky.data.result.ProTableResult; import org.dinky.mapper.FragmentVariableMapper; import org.dinky.mybatis.service.impl.SuperServiceImpl; import org.dinky.service.FragmentVariableService; +import org.dinky.utils.FragmentVariableUtils; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import javax.annotation.Resource; + import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.databind.JsonNode; /** FragmentVariableServiceImpl */ @Service @@ -38,6 +45,47 @@ public class FragmentVariableServiceImpl extends SuperServiceImpl implements FragmentVariableService { + @Resource private CryptoComponent cryptoComponent; + + @Override + public boolean saveOrUpdate(FragmentVariable entity) { + if (FragmentVariableUtils.isSensitive(entity.getName()) + && entity.getFragmentValue() != null) { + entity.setFragmentValue(cryptoComponent.encryptText(entity.getFragmentValue())); + } + return super.saveOrUpdate(entity); + } + + @Override + public List list(Wrapper queryWrapper) { + final List list = super.list(queryWrapper); + if (list != null) { + for (FragmentVariable variable : list) { + if (FragmentVariableUtils.isSensitive(variable.getName()) + && variable.getFragmentValue() != null) { + variable.setFragmentValue( + cryptoComponent.decryptText(variable.getFragmentValue())); + } + } + } + return list; + } + + @Override + public ProTableResult selectForProTable(JsonNode para) { + final ProTableResult result = super.selectForProTable(para); + if (result != null && result.getData() != null) { + for (FragmentVariable variable : result.getData()) { + if (FragmentVariableUtils.isSensitive(variable.getName()) + && variable.getFragmentValue() != null) { + variable.setFragmentValue( + cryptoComponent.decryptText(variable.getFragmentValue())); + } + } + } + return super.selectForProTable(para); + } + @Override public List listEnabledAll() { return list(new LambdaQueryWrapper().eq(FragmentVariable::getEnabled, 1)); diff --git a/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java index 997aedde67..281ca36d9e 100644 --- a/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java +++ b/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java @@ -109,6 +109,7 @@ import org.dinky.service.UDFTemplateService; import org.dinky.service.UserService; import org.dinky.utils.DockerClientUtils; +import org.dinky.utils.FragmentVariableUtils; import org.dinky.utils.JSONUtil; import org.dinky.utils.UDFUtils; @@ -629,6 +630,16 @@ public String exportSql(Integer id) { } JobConfig config = buildJobConfig(task); + + // 加密敏感信息 + if (config.getVariables() != null) { + for (Map.Entry entry : config.getVariables().entrySet()) { + if (FragmentVariableUtils.isSensitive(entry.getKey())) { + entry.setValue(FragmentVariableUtils.HIDDEN_CONTENT); + } + } + } + JobManager jobManager = JobManager.build(config); if (config.isJarTask()) { return ""; diff --git a/dinky-admin/src/main/java/org/dinky/utils/FragmentVariableUtils.java b/dinky-admin/src/main/java/org/dinky/utils/FragmentVariableUtils.java new file mode 100644 index 0000000000..3c3b03e814 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/utils/FragmentVariableUtils.java @@ -0,0 +1,48 @@ +/* + * + * 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.dinky.utils; + +public class FragmentVariableUtils { + + /** 隐藏占位符 */ + public static final String HIDDEN_CONTENT = "******"; + + private static final String[] SENSITIVE_KEYS = + new String[] {"password", "secret", "fs.azure.account.key", "apikey"}; + + /** + * 判断变量是否敏感 + * + * @param key 变量名 + * @return 敏感返回true + */ + public static boolean isSensitive(String key) { + if (key == null) { + return false; + } + final String keyInLower = key.toLowerCase(); + for (String hideKey : SENSITIVE_KEYS) { + if (keyInLower.length() >= hideKey.length() && keyInLower.contains(hideKey)) { + return true; + } + } + return false; + } +} diff --git a/dinky-admin/src/main/resources/application.yml b/dinky-admin/src/main/resources/application.yml index e16ae6dacf..5d8aa9496c 100644 --- a/dinky-admin/src/main/resources/application.yml +++ b/dinky-admin/src/main/resources/application.yml @@ -165,4 +165,11 @@ knife4j: Dinky: version: @project.version@ sms: - is-print: false \ No newline at end of file + is-print: false + +################################################################################################################# +################################################# Crypto Config ################################################# +################################################################################################################# +crypto: + enabled: false + encryption-password: diff --git a/dinky-admin/src/main/resources/db/db-h2.sql b/dinky-admin/src/main/resources/db/db-h2.sql index 9a4e40fa37..5ac0970c9b 100644 --- a/dinky-admin/src/main/resources/db/db-h2.sql +++ b/dinky-admin/src/main/resources/db/db-h2.sql @@ -91,7 +91,7 @@ CREATE TABLE `dinky_database` ( `port` int(11) NULL DEFAULT NULL COMMENT 'database port', `url` varchar(255) NULL DEFAULT NULL COMMENT 'database url', `username` varchar(50) NULL DEFAULT NULL COMMENT 'username', - `password` varchar(50) NULL DEFAULT NULL COMMENT 'password', + `password` varchar(512) NULL DEFAULT NULL COMMENT 'password', `note` varchar(255) NULL DEFAULT NULL COMMENT 'note', `flink_config` text NULL COMMENT 'Flink configuration', `flink_template` text NULL COMMENT 'Flink template', diff --git a/pom.xml b/pom.xml index 64df13b35f..f388951602 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,7 @@ 5.8.11 2.14.1 2.1.6 + 1.9.3 1.1.1 1.3.2 1.6.2 @@ -148,6 +149,11 @@ assertj-core ${assertj.version} + + org.jasypt + jasypt + ${jasypt.version} + org.mockito diff --git a/script/sql/dinky-mysql.sql b/script/sql/dinky-mysql.sql index 38813ad7f0..61e3b0190b 100644 --- a/script/sql/dinky-mysql.sql +++ b/script/sql/dinky-mysql.sql @@ -173,7 +173,7 @@ CREATE TABLE `dinky_database` ( `port` int(11) NULL DEFAULT NULL COMMENT 'database port', `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'database url', `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'username', - `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'password', + `password` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'password', `note` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'note', `flink_config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'Flink configuration', `flink_template` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'Flink template', diff --git a/script/sql/dinky-pg.sql b/script/sql/dinky-pg.sql index 8fd1f2dda1..07b615957d 100644 --- a/script/sql/dinky-pg.sql +++ b/script/sql/dinky-pg.sql @@ -252,7 +252,7 @@ CREATE TABLE "public"."dinky_database" ( "port" int4, "url" varchar(255) COLLATE "pg_catalog"."default", "username" varchar(50) COLLATE "pg_catalog"."default", - "password" varchar(50) COLLATE "pg_catalog"."default", + "password" varchar(512) COLLATE "pg_catalog"."default", "note" varchar(255) COLLATE "pg_catalog"."default", "flink_config" text COLLATE "pg_catalog"."default", "flink_template" text COLLATE "pg_catalog"."default", diff --git a/script/sql/upgrade/1.0.0-SNAPSHOT_schema/mysql/dinky_ddl.sql b/script/sql/upgrade/1.0.0-SNAPSHOT_schema/mysql/dinky_ddl.sql index f53548f6f3..1d2fa0cd8c 100644 --- a/script/sql/upgrade/1.0.0-SNAPSHOT_schema/mysql/dinky_ddl.sql +++ b/script/sql/upgrade/1.0.0-SNAPSHOT_schema/mysql/dinky_ddl.sql @@ -171,7 +171,7 @@ CREATE TABLE `dinky_sys_login_log` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='system login log record' - +ALTER TABLE dinky_database modify password varchar(512) null comment 'password';