diff --git a/common/src/main/java/org/apache/hertzbeat/common/constants/DesensitizedField.java b/common/src/main/java/org/apache/hertzbeat/common/constants/DesensitizedField.java new file mode 100644 index 00000000000..d78040af098 --- /dev/null +++ b/common/src/main/java/org/apache/hertzbeat/common/constants/DesensitizedField.java @@ -0,0 +1,34 @@ +/* + * 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.hertzbeat.common.constants; + +import org.apache.hertzbeat.common.util.DesensitizedUtil; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({ FIELD}) +@Retention(RUNTIME) +@Documented +public @interface DesensitizedField { + DesensitizedUtil.DesensitizedType desensitizedType(); +} diff --git a/common/src/main/java/org/apache/hertzbeat/common/entity/manager/NoticeReceiver.java b/common/src/main/java/org/apache/hertzbeat/common/entity/manager/NoticeReceiver.java index 28320977de7..01ac4017a41 100644 --- a/common/src/main/java/org/apache/hertzbeat/common/entity/manager/NoticeReceiver.java +++ b/common/src/main/java/org/apache/hertzbeat/common/entity/manager/NoticeReceiver.java @@ -37,6 +37,8 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.apache.hertzbeat.common.constants.DesensitizedField; +import org.apache.hertzbeat.common.util.DesensitizedUtil; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; @@ -84,12 +86,14 @@ public class NoticeReceiver { description = "Mobile number: Valid when the notification method is SMS", example = "18923435643", accessMode = READ_WRITE) @Size(max = 100) + @DesensitizedField(desensitizedType= DesensitizedUtil.DesensitizedType.MOBILE_PHONE) private String phone; @Schema(title = "Email account: Valid when the notification method is email", description = "Email account: Valid when the notification method is email", example = "tom@qq.com", accessMode = READ_WRITE) @Size(max = 100) + @DesensitizedField(desensitizedType= DesensitizedUtil.DesensitizedType.EMAIL) private String email; @Schema(title = "URL address: The notification method is valid for webhook", @@ -148,6 +152,7 @@ public class NoticeReceiver { @Schema(title = "Enterprise weChat secret: The notification method is valid for Enterprise WeChat app message", description = "Enterprise weChat secret: The notification method is valid for Enterprise WeChat app message", example = "oUydwn92ey0lnuY02MixNa57eNK-20dJn5NEOG-u2uE", accessMode = READ_WRITE) + @DesensitizedField(desensitizedType= DesensitizedUtil.DesensitizedType.PASSWORD) private String appSecret; @Schema(title = "Enterprise weChat party id: The notification method is valid for Enterprise WeChat app message", diff --git a/common/src/main/java/org/apache/hertzbeat/common/util/DesensitizedUtil.java b/common/src/main/java/org/apache/hertzbeat/common/util/DesensitizedUtil.java index 69b93fe8bb2..28b87ffcb0c 100644 --- a/common/src/main/java/org/apache/hertzbeat/common/util/DesensitizedUtil.java +++ b/common/src/main/java/org/apache/hertzbeat/common/util/DesensitizedUtil.java @@ -24,6 +24,34 @@ */ public class DesensitizedUtil { + /** + * desensitize field + */ + public enum DesensitizedType { + MOBILE_PHONE, + EMAIL, + PASSWORD + } + + /** + * desensitize field + * @param type field type + * @param str field value + * @return desensitized value + */ + public static String desensitize(DesensitizedType type, String str) { + if (type == null || StringUtils.isEmpty(str)) { + return str; + } + switch (type) { + case MOBILE_PHONE -> str = mobilePhone(str); + case EMAIL -> str = email(str); + case PASSWORD -> str = password(str); + default -> {} + } + return str; + } + /** * desensitize mobile phone * @param str field value diff --git a/manager/src/main/java/org/apache/hertzbeat/manager/aspect/DesensitizedAspect.java b/manager/src/main/java/org/apache/hertzbeat/manager/aspect/DesensitizedAspect.java new file mode 100644 index 00000000000..3d203367c24 --- /dev/null +++ b/manager/src/main/java/org/apache/hertzbeat/manager/aspect/DesensitizedAspect.java @@ -0,0 +1,113 @@ +/* + * 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.hertzbeat.manager.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.apache.hertzbeat.common.constants.DesensitizedField; +import org.apache.hertzbeat.common.entity.manager.NoticeReceiver; +import org.apache.hertzbeat.common.util.DesensitizedUtil; +import org.apache.hertzbeat.manager.dao.NoticeReceiverDao; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.lang.reflect.Field; +import java.util.List; + +@Aspect +@Component +@Slf4j +public class DesensitizedAspect { + @Resource + private NoticeReceiverDao noticeReceiverDao; + + @Around("execution(* org.apache.hertzbeat.manager.service.NoticeConfigService.*(..)))") + public Object around(ProceedingJoinPoint point) { + try { + unDesensitized(point.getArgs()); + Object result = point.proceed(); + return desensitized(result); + } catch (Throwable e) { + log.error(e.getMessage(), e); + } + return null; + } + + private void unDesensitized(Object[] args) throws IllegalAccessException { + if (args == null || args.length == 0) { + return; + } + Long receiverId = null; + for (Object arg : args) { + if (arg instanceof NoticeReceiver argNoticeReceiver) { + receiverId = argNoticeReceiver.getId(); + break; + } + } + if (receiverId == null) { + return; + } + NoticeReceiver noticeReceiver = noticeReceiverDao.findById(receiverId).orElse(null); + if (noticeReceiver == null) { + return; + } + for (Object arg : args) { + if (arg instanceof NoticeReceiver argNoticeReceiver) { + for (Field field : argNoticeReceiver.getClass().getDeclaredFields()) { + DesensitizedField annotation = field.getAnnotation(DesensitizedField.class); + if (annotation != null) { + field.setAccessible(true); + DesensitizedUtil.DesensitizedType desensitizedType = annotation.desensitizedType(); + String desensitizedValue = DesensitizedUtil.desensitize(desensitizedType, field.get(noticeReceiver).toString()); + if (field.get(argNoticeReceiver) != null && field.get(argNoticeReceiver).equals(desensitizedValue)) { + field.set(argNoticeReceiver, field.get(noticeReceiver)); + } + } + } + } + } + } + + private Object desensitized(Object result) throws IllegalAccessException { + if (result == null) { + return null; + } + if (result instanceof List) { + for (Object item : ((List) result)) { + desensitizedField(item); + } + } else { + desensitizedField(result); + } + return result; + } + + private void desensitizedField(Object result) throws IllegalAccessException { + for (Field field : result.getClass().getDeclaredFields()) { + DesensitizedField annotation = field.getAnnotation(DesensitizedField.class); + if (annotation != null) { + DesensitizedUtil.DesensitizedType desensitizedType = annotation.desensitizedType(); + field.setAccessible(true); + field.set(result, DesensitizedUtil.desensitize(desensitizedType, field.get(result).toString())); + } + } + } + +} diff --git a/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java b/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java index 29adebe093c..b11f2af5068 100644 --- a/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java +++ b/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java @@ -40,7 +40,6 @@ import org.apache.hertzbeat.common.entity.manager.NoticeReceiver; import org.apache.hertzbeat.common.entity.manager.NoticeRule; import org.apache.hertzbeat.common.entity.manager.NoticeTemplate; -import org.apache.hertzbeat.common.util.DesensitizedUtil; import org.apache.hertzbeat.manager.component.alerter.DispatcherAlarm; import org.apache.hertzbeat.manager.dao.NoticeReceiverDao; import org.apache.hertzbeat.manager.dao.NoticeRuleDao; @@ -96,15 +95,7 @@ public List getNoticeReceivers(String name) { } return predicate; }; - List noticeReceivers = noticeReceiverDao.findAll(specification); - if (CollectionUtils.isNotEmpty(noticeReceivers)) { - noticeReceivers.forEach(noticeReceiver -> { - noticeReceiver.setPhone(DesensitizedUtil.mobilePhone(noticeReceiver.getPhone())); - noticeReceiver.setEmail(DesensitizedUtil.email(noticeReceiver.getEmail())); - noticeReceiver.setAppSecret(DesensitizedUtil.password(noticeReceiver.getAppSecret())); - }); - } - return noticeReceivers; + return noticeReceiverDao.findAll(specification); } @Override