From 7b1744baf505e4b1a8f5737f0a4cd1fa6286c10c Mon Sep 17 00:00:00 2001 From: Kerwin <37063904+zhuangchong@users.noreply.github.com> Date: Wed, 12 Jan 2022 18:13:55 +0800 Subject: [PATCH] cherry-pick DingTalk alert plugin adds signature. #7805 (#7959) --- .../dingtalk/DingTalkAlertChannelFactory.java | 68 ++++++++++++------- .../dingtalk/DingTalkParamsConstants.java | 8 ++- .../plugin/alert/dingtalk/DingTalkSender.java | 57 +++++++++++++--- .../DingTalkAlertChannelFactoryTest.java | 2 +- .../src/js/module/i18n/locale/en_US.js | 1 + .../src/js/module/i18n/locale/zh_CN.js | 3 +- 6 files changed, 102 insertions(+), 37 deletions(-) diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java index 2050eaae40..01b9060204 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java @@ -50,46 +50,62 @@ public final class DingTalkAlertChannelFactory implements AlertChannelFactory { @Override public List params() { - InputParam webHookParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK, DingTalkParamsConstants.DING_TALK_WEB_HOOK) + + InputParam webHookParam = InputParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK, DingTalkParamsConstants.DING_TALK_WEB_HOOK) .addValidate(Validate.newBuilder() .setRequired(true) .build()) .build(); - InputParam keywordParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD, DingTalkParamsConstants.DING_TALK_KEYWORD) + + InputParam keywordParam = InputParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD, DingTalkParamsConstants.DING_TALK_KEYWORD) .addValidate(Validate.newBuilder() - .setRequired(true) + .setRequired(false) .build()) .build(); - RadioParam isEnableProxy = - RadioParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE) - .addParamsOptions(new ParamsOptions(STRING_YES, STRING_TRUE, false)) - .addParamsOptions(new ParamsOptions(STRING_NO, STRING_FALSE, false)) - .setValue(STRING_TRUE) - .addValidate(Validate.newBuilder() - .setRequired(false) - .build()) - .build(); - InputParam proxyParam = - InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY, DingTalkParamsConstants.DING_TALK_PROXY) - .addValidate(Validate.newBuilder() - .setRequired(false).build()) - .build(); - InputParam portParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PORT, DingTalkParamsConstants.DING_TALK_PORT) + InputParam secretParam = InputParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_SECRET, DingTalkParamsConstants.DING_TALK_SECRET) .addValidate(Validate.newBuilder() - .setRequired(false).build()) + .setRequired(false) + .build()) + .build(); + RadioParam isEnableProxy = RadioParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, DingTalkParamsConstants.DING_TALK_PROXY_ENABLE) + .addParamsOptions(new ParamsOptions(STRING_YES, STRING_TRUE, false)) + .addParamsOptions(new ParamsOptions(STRING_NO, STRING_FALSE, false)) + .setValue(STRING_FALSE) + .addValidate(Validate.newBuilder() + .setRequired(false) + .build()) + .build(); + InputParam proxyParam = InputParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY, DingTalkParamsConstants.DING_TALK_PROXY) + .addValidate(Validate.newBuilder() + .setRequired(false) + .build()) + .build(); + + InputParam portParam = InputParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PORT, DingTalkParamsConstants.DING_TALK_PORT) + .addValidate(Validate.newBuilder() + .setRequired(false) + .build()) .build(); - InputParam userParam = - InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_USER, DingTalkParamsConstants.DING_TALK_USER) - .addValidate(Validate.newBuilder() - .setRequired(false).build()) - .build(); - PasswordParam passwordParam = PasswordParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD, DingTalkParamsConstants.DING_TALK_PASSWORD) + InputParam userParam = InputParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_USER, DingTalkParamsConstants.DING_TALK_USER) + .addValidate(Validate.newBuilder() + .setRequired(false) + .build()) + .build(); + PasswordParam passwordParam = PasswordParam + .newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD, DingTalkParamsConstants.DING_TALK_PASSWORD) .setPlaceholder("if enable use authentication, you need input password") .build(); - return Arrays.asList(webHookParam, keywordParam, isEnableProxy, proxyParam, portParam, userParam, passwordParam); + return Arrays.asList(webHookParam, keywordParam, secretParam, isEnableProxy, proxyParam, portParam, userParam, passwordParam); } } diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java index 3042db6286..fa47b0c87b 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java @@ -18,12 +18,18 @@ package org.apache.dolphinscheduler.plugin.alert.dingtalk; public final class DingTalkParamsConstants { + static final String DING_TALK_PROXY_ENABLE = "isEnableProxy"; + static final String NAME_DING_TALK_PROXY_ENABLE = "IsEnableProxy"; + static final String DING_TALK_WEB_HOOK = "webhook"; static final String NAME_DING_TALK_WEB_HOOK = "WebHook"; static final String DING_TALK_KEYWORD = "keyword"; static final String NAME_DING_TALK_KEYWORD = "Keyword"; - static final String NAME_DING_TALK_PROXY_ENABLE = "IsEnableProxy"; + + static final String DING_TALK_SECRET = "secret"; + static final String NAME_DING_TALK_SECRET = "Secret"; + static final String DING_TALK_PROXY = "proxy"; static final String NAME_DING_TALK_PROXY = "Proxy"; static final String DING_TALK_PORT = "port"; diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java index f5d5f1428d..eeca8c03c3 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java @@ -20,6 +20,7 @@ package org.apache.dolphinscheduler.plugin.alert.dingtalk; import org.apache.dolphinscheduler.alert.api.AlertResult; import org.apache.dolphinscheduler.spi.utils.JSONUtils; +import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -36,16 +37,30 @@ import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.IOException; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + *

+ * https://open.dingtalk.com/document/robots/custom-robot-access + * https://open.dingtalk.com/document/robots/customize-robot-security-settings + *

+ */ public final class DingTalkSender { - private static final Logger log = org.slf4j.LoggerFactory.getLogger(DingTalkSender.class); + + private static final Logger logger = LoggerFactory.getLogger(DingTalkSender.class); + private final String url; private final String keyword; + private final String secret; private final Boolean enableProxy; private String proxy; @@ -59,6 +74,7 @@ public final class DingTalkSender { DingTalkSender(Map config) { url = config.get(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK); keyword = config.get(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD); + secret = config.get(DingTalkParamsConstants.NAME_DING_TALK_SECRET); enableProxy = Boolean.valueOf(config.get(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE)); if (Boolean.TRUE.equals(enableProxy)) { port = Integer.parseInt(config.get(DingTalkParamsConstants.NAME_DING_TALK_PORT)); @@ -109,13 +125,13 @@ public final class DingTalkSender { if (null == result) { alertResult.setMessage("send ding talk msg error"); - log.info("send ding talk msg error,ding talk server resp is null"); + logger.info("send ding talk msg error,ding talk server resp is null"); return alertResult; } DingTalkSendMsgResponse sendMsgResponse = JSONUtils.parseObject(result, DingTalkSendMsgResponse.class); if (null == sendMsgResponse) { alertResult.setMessage("send ding talk msg fail"); - log.info("send ding talk msg error,resp error"); + logger.info("send ding talk msg error,resp error"); return alertResult; } if (sendMsgResponse.errcode == 0) { @@ -124,7 +140,7 @@ public final class DingTalkSender { return alertResult; } alertResult.setMessage(String.format("alert send ding talk msg error : %s", sendMsgResponse.getErrmsg())); - log.info("alert send ding talk msg error : {}", sendMsgResponse.getErrmsg()); + logger.info("alert send ding talk msg error : {}", sendMsgResponse.getErrmsg()); return alertResult; } @@ -134,7 +150,7 @@ public final class DingTalkSender { String resp = sendMsg(title, content); return checkSendDingTalkSendMsgResult(resp); } catch (Exception e) { - log.info("send ding talk alert msg exception : {}", e.getMessage()); + logger.info("send ding talk alert msg exception : {}", e.getMessage()); alertResult = new AlertResult(); alertResult.setStatus("false"); alertResult.setMessage("send ding talk alert fail."); @@ -144,8 +160,18 @@ public final class DingTalkSender { private String sendMsg(String title, String content) throws IOException { - String msgToJson = textToJsonString(title + content + "#" + keyword); - HttpPost httpPost = constructHttpPost(url, msgToJson); + StringBuilder text = new StringBuilder(); + if (org.apache.dolphinscheduler.spi.utils.StringUtils.isNotBlank(keyword)) { + text.append(keyword); + text.append(":"); + } + text.append(title); + text.append("\n"); + text.append(content); + + String msgToJson = textToJsonString(text.toString()); + + HttpPost httpPost = constructHttpPost(org.apache.dolphinscheduler.spi.utils.StringUtils.isBlank(secret) ? url : generateSignedUrl(), msgToJson); CloseableHttpClient httpClient; if (Boolean.TRUE.equals(enableProxy)) { @@ -166,13 +192,28 @@ public final class DingTalkSender { } finally { response.close(); } - log.info("Ding Talk send title :{},content : {}, resp: {}", title, content, resp); + logger.info("Ding Talk send title :{},content : {}, resp: {}", title, content, resp); return resp; } finally { httpClient.close(); } } + private String generateSignedUrl() { + Long timestamp = System.currentTimeMillis(); + String stringToSign = timestamp + "\n" + secret; + String sign = org.apache.dolphinscheduler.spi.utils.StringUtils.EMPTY; + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")); + byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8")); + sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8"); + } catch (Exception e) { + logger.error("generate sign error, message:{}", e); + } + return url + "×tamp=" + timestamp + "&sign=" + sign; + } + static final class DingTalkSendMsgResponse { private Integer errcode; private String errmsg; diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java index 8b780427fc..99051952e5 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java +++ b/dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java @@ -32,7 +32,7 @@ public class DingTalkAlertChannelFactoryTest { DingTalkAlertChannelFactory dingTalkAlertChannelFactory = new DingTalkAlertChannelFactory(); List params = dingTalkAlertChannelFactory.params(); JSONUtils.toJsonString(params); - Assert.assertEquals(7, params.size()); + Assert.assertEquals(8, params.size()); } @Test diff --git a/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js b/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js index e7abaf6691..c749142e00 100755 --- a/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js +++ b/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js @@ -726,6 +726,7 @@ export default { WebHook: 'WebHook', webHook: 'WebHook', Keyword: 'Keyword', + Secret: 'Secret', Proxy: 'Proxy', receivers: 'Receivers', receiverCcs: 'ReceiverCcs', diff --git a/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js b/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js index 70a71f146b..4d62bb9aac 100644 --- a/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js +++ b/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js @@ -724,7 +724,8 @@ export default { IsEnableProxy: '启用代理', WebHook: 'Web钩子', webHook: 'Web钩子', - Keyword: '密钥', + Keyword: '关键词', + Secret: '密钥', Proxy: '代理', receivers: '收件人', receiverCcs: '抄送人',