diff --git a/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java b/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java index 0480357a..52f955fc 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/serialize/JsonUtils.java @@ -3,6 +3,7 @@ package tech.powerjob.common.serialize; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.json.JsonMapper; import lombok.extern.slf4j.Slf4j; @@ -28,6 +29,7 @@ public class JsonUtils { .configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) .configure(JsonParser.Feature.IGNORE_UNDEFINED, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .build(); private static final TypeReference> MAP_TYPE_REFERENCE = new TypeReference> () {}; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthConstants.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthConstants.java index 19fcaa40..770b8fc2 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthConstants.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthConstants.java @@ -8,6 +8,26 @@ package tech.powerjob.server.auth.common; */ public class AuthConstants { + /* ********** 账号体系唯一标识,推荐开发者接入第三方登录体系时也使用4位编码,便于前端统一做样式 ********** */ + /** + * PowerJob自建账号体系 + */ + public static final String ACCOUNT_TYPE_POWER_JOB = "PWJB"; + /** + * 钉钉 + */ + public static final String ACCOUNT_TYPE_DING = "DING"; + /** + * 企业微信(预留,蹲一个 contributor) + */ + public static final String ACCOUNT_TYPE_WX = "QYWX"; + /** + * 飞书(预留,蹲一个 contributor +1) + */ + public static final String ACCOUNT_LARK = "LARK"; + + /* ********** 账号体系 ********** */ + /** * JWT key * 前端 header 默认首字母大写,保持一致方便处理 diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthErrorCode.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthErrorCode.java index 254dbd06..be64f578 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthErrorCode.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/AuthErrorCode.java @@ -22,7 +22,9 @@ public enum AuthErrorCode { /** * 无效请求,一般是参数问题 */ - INVALID_REQUEST("-300", "INVALID_REQUEST") + INVALID_REQUEST("-300", "INVALID_REQUEST"), + + INCORRECT_PASSWORD("-400", "INCORRECT_PASSWORD") ; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyUser.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyUser.java index e5d2e739..bf2bca28 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyUser.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyUser.java @@ -31,6 +31,10 @@ public class ThirdPartyUser { * 邮箱地址 */ private String email; + /** + * web 回调地址 + */ + private String webHook; /** * 扩展字段 */ diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkLoginService.java index 539f2ab7..afb0a80a 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkLoginService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkLoginService.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.server.auth.common.AuthConstants; import tech.powerjob.server.auth.login.*; import tech.powerjob.server.common.Loggers; @@ -56,13 +57,11 @@ public class DingTalkLoginService implements ThirdPartyLoginService { @Value("${oms.auth.dingtalk.callbackUrl:#{null}}") private String dingTalkCallbackUrl; - private static final String DING_TALK = "DingTalk"; - @Override public LoginTypeInfo loginType() { return new LoginTypeInfo() - .setType(DING_TALK) - .setName("钉钉登录") + .setType(AuthConstants.ACCOUNT_TYPE_DING) + .setName("DingTalkLogin") ; } @@ -79,7 +78,7 @@ public class DingTalkLoginService implements ThirdPartyLoginService { "&response_type=code" + "&client_id=" + dingTalkAppKey + "&scope=openid" + - "&state=" + DING_TALK + + "&state=" + AuthConstants.ACCOUNT_TYPE_DING + "&prompt=consent"; Loggers.WEB.info("[DingTalkBizLoginService] login url: {}", url); return url; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobThirdPartyLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobThirdPartyLoginService.java index 43b76abd..738820ed 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobThirdPartyLoginService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobThirdPartyLoginService.java @@ -14,8 +14,8 @@ import tech.powerjob.server.auth.login.ThirdPartyLoginService; import tech.powerjob.server.auth.login.ThirdPartyUser; import tech.powerjob.server.common.Loggers; import tech.powerjob.server.common.utils.DigestUtils; -import tech.powerjob.server.persistence.remote.model.UserInfoDO; -import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; +import tech.powerjob.server.persistence.remote.model.PwjbUserInfoDO; +import tech.powerjob.server.persistence.remote.repository.PwjbUserInfoRepository; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @@ -33,9 +33,7 @@ import java.util.Optional; public class PowerJobThirdPartyLoginService implements ThirdPartyLoginService { @Resource - private UserInfoRepository userInfoRepository; - - private static final String POWER_JOB_LOGIN_SERVICE = "PowerJob"; + private PwjbUserInfoRepository pwjbUserInfoRepository; private static final String KEY_USERNAME = "username"; private static final String KEY_PASSWORD = "password"; @@ -45,8 +43,8 @@ public class PowerJobThirdPartyLoginService implements ThirdPartyLoginService { @Override public LoginTypeInfo loginType() { return new LoginTypeInfo() - .setType(POWER_JOB_LOGIN_SERVICE) - .setName("PowerJob") + .setType(AuthConstants.ACCOUNT_TYPE_POWER_JOB) + .setName("PowerJob Built-in Login") ; } @@ -74,17 +72,30 @@ public class PowerJobThirdPartyLoginService implements ThirdPartyLoginService { throw new PowerJobAuthException(AuthErrorCode.INVALID_REQUEST); } - final Optional userInfoOpt = userInfoRepository.findByUsername(username); + final Optional userInfoOpt = pwjbUserInfoRepository.findByUsername(username); if (!userInfoOpt.isPresent()) { Loggers.WEB.debug("[PowerJobLoginService] can't find user by username: {}", username); throw new PowerJobAuthException(AuthErrorCode.USER_NOT_EXIST); } - final UserInfoDO dbUser = userInfoOpt.get(); + final PwjbUserInfoDO dbUser = userInfoOpt.get(); if (DigestUtils.rePassword(password, username).equals(dbUser.getPassword())) { ThirdPartyUser bizUser = new ThirdPartyUser(); bizUser.setUsername(username); + + // 回填第一次创建的信息 + String extra = dbUser.getExtra(); + if (StringUtils.isNotEmpty(extra)) { + ThirdPartyUser material = JsonUtils.parseObjectIgnoreException(extra, ThirdPartyUser.class); + if (material != null) { + bizUser.setEmail(material.getEmail()); + bizUser.setNick(material.getNick()); + bizUser.setPhone(material.getPhone()); + bizUser.setWebHook(material.getWebHook()); + } + } + return bizUser; } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java index f20673cc..2599bb94 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import tech.powerjob.server.auth.LoginUserHolder; import tech.powerjob.server.auth.PowerJobUser; import tech.powerjob.server.auth.common.AuthConstants; import tech.powerjob.server.auth.common.AuthErrorCode; @@ -91,6 +92,16 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService { if (!powerJobUserOpt.isPresent()) { UserInfoDO newUser = new UserInfoDO(); newUser.setUsername(dbUserName); + // 写入账号体系类型 + newUser.setAccountType(loginType); + + // 同步素材 + newUser.setEmail(bizUser.getEmail()); + newUser.setPhone(bizUser.getPhone()); + newUser.setNick(bizUser.getNick()); + newUser.setWebHook(bizUser.getWebHook()); + newUser.setExtra(bizUser.getExtra()); + Loggers.WEB.info("[PowerJobLoginService] sync user to PowerJobUserSystem: {}", dbUserName); userInfoRepository.saveAndFlush(newUser); @@ -117,6 +128,10 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService { return userNameOpt.flatMap(uname -> userInfoRepository.findByUsername(uname).map(userInfoDO -> { PowerJobUser powerJobUser = new PowerJobUser(); BeanUtils.copyProperties(userInfoDO, powerJobUser); + + // 兼容某些直接通过 ifLogin 判断登录的场景 + LoginUserHolder.set(powerJobUser); + return powerJobUser; })); } diff --git a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/UserService.java b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/UserService.java index 21dc50c7..4087438c 100644 --- a/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/UserService.java +++ b/powerjob-server/powerjob-server-core/src/main/java/tech/powerjob/server/core/service/UserService.java @@ -1,16 +1,13 @@ package tech.powerjob.server.core.service; -import tech.powerjob.common.utils.CommonUtils; -import tech.powerjob.server.common.utils.DigestUtils; -import tech.powerjob.server.persistence.remote.model.UserInfoDO; -import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; import com.google.common.base.Splitter; import com.google.common.collect.Lists; -import org.springframework.stereotype.Service; import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import tech.powerjob.server.persistence.remote.model.UserInfoDO; +import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; import javax.annotation.Resource; -import java.util.Date; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -27,26 +24,6 @@ public class UserService { @Resource private UserInfoRepository userInfoRepository; - /** - * 保存/修改 用户 - * @param userInfoDO user - */ - public void save(UserInfoDO userInfoDO) { - - CommonUtils.requireNonNull(userInfoDO.getUsername(), "userName can't be null or empty!"); - - userInfoDO.setGmtCreate(new Date()); - userInfoDO.setGmtModified(userInfoDO.getGmtCreate()); - - // 二次加密密码 - final String password = userInfoDO.getPassword(); - if (StringUtils.isNotEmpty(password)) { - userInfoDO.setPassword(DigestUtils.rePassword(password, userInfoDO.getUsername())); - } - - userInfoRepository.saveAndFlush(userInfoDO); - } - /** * 根据用户ID字符串获取用户信息详细列表 * @param userIds 逗号分割的用户ID信息 diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/PwjbUserInfoDO.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/PwjbUserInfoDO.java new file mode 100644 index 00000000..81418271 --- /dev/null +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/PwjbUserInfoDO.java @@ -0,0 +1,36 @@ +package tech.powerjob.server.persistence.remote.model; + +import lombok.Data; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.Date; + +/** + * PowerJob 自建登录体系的用户表,只存储使用 PowerJob 自带登录方式登录的用户信息 + * + * @author tjq + * @since 2024/2/13 + */ +@Data +@Entity +@Table(uniqueConstraints = { + @UniqueConstraint(name = "uidx01_username", columnNames = {"username"}) +}) +public class PwjbUserInfoDO { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") + @GenericGenerator(name = "native", strategy = "native") + private Long id; + + private String username; + + private String password; + + private String extra; + + private Date gmtCreate; + + private Date gmtModified; +} diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java index 21df9364..a8b81bd6 100644 --- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java @@ -8,6 +8,7 @@ import java.util.Date; /** * 用户信息表 + * PowerJob 自身维护的全部用户体系数据 * 5.0.0 可能不兼容改动:为了支持第三方登录,需要通过 username 与第三方登录系统做匹配,该列需要声明为唯一索引,确保全局唯一 * * @author tjq @@ -28,6 +29,11 @@ public class UserInfoDO { @GenericGenerator(name = "native", strategy = "native") private Long id; + /** + * 账号类型 + */ + private String accountType; + private String username; /** * since 5.0.0 diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/AppInfoRepository.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/AppInfoRepository.java index 684b0d56..ec98e8e9 100644 --- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/AppInfoRepository.java +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/AppInfoRepository.java @@ -8,6 +8,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import tech.powerjob.server.persistence.remote.model.AppInfoDO; +import java.util.Collection; import java.util.List; import java.util.Optional; @@ -34,4 +35,6 @@ public interface AppInfoRepository extends JpaRepository, JpaSp List findAllByNamespaceId(Long namespaceId); + + List findAllByIdIn(Collection ids); } diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java index db7f8e14..ff776903 100644 --- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java @@ -4,6 +4,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import tech.powerjob.server.persistence.remote.model.NamespaceDO; +import java.util.Collection; +import java.util.List; import java.util.Optional; /** @@ -15,4 +17,6 @@ import java.util.Optional; public interface NamespaceRepository extends JpaRepository, JpaSpecificationExecutor { Optional findByCode(String code); + + List findAllByIdIn(Collection ids); } diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/PwjbUserInfoRepository.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/PwjbUserInfoRepository.java new file mode 100644 index 00000000..68c02bb0 --- /dev/null +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/PwjbUserInfoRepository.java @@ -0,0 +1,17 @@ +package tech.powerjob.server.persistence.remote.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import tech.powerjob.server.persistence.remote.model.PwjbUserInfoDO; + +import java.util.Optional; + +/** + * PwjbUserInfoRepository + * + * @author tjq + * @since 2024/2/13 + */ +public interface PwjbUserInfoRepository extends JpaRepository { + + Optional findByUsername(String username); +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/WebAuthService.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/WebAuthService.java index 63c812f7..a2b8d7a8 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/WebAuthService.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/WebAuthService.java @@ -1,10 +1,12 @@ package tech.powerjob.server.auth.service; import tech.powerjob.server.auth.Permission; +import tech.powerjob.server.auth.Role; import tech.powerjob.server.auth.RoleScope; import tech.powerjob.server.web.request.ComponentUserRoleInfo; -import java.util.Set; +import java.util.List; +import java.util.Map; /** * Web Auth 服务 @@ -41,5 +43,5 @@ public interface WebAuthService { */ boolean hasPermission(RoleScope roleScope, Long target, Permission permission); - Set fetchMyPermissionTargets(RoleScope roleScope); + Map> fetchMyPermissionTargets(RoleScope roleScope); } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/impl/WebAuthServiceImpl.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/impl/WebAuthServiceImpl.java index bdc2ac27..568ebad3 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/impl/WebAuthServiceImpl.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/service/impl/WebAuthServiceImpl.java @@ -62,20 +62,15 @@ public class WebAuthServiceImpl implements WebAuthService { } @Override - public Set fetchMyPermissionTargets(RoleScope roleScope) { + public Map> fetchMyPermissionTargets(RoleScope roleScope) { PowerJobUser powerJobUser = LoginUserHolder.get(); if (powerJobUser == null) { throw new PowerJobAuthException(AuthErrorCode.USER_NOT_LOGIN); } - Map> role2TargetIds = powerJobPermissionService.fetchUserHadPermissionTargets(roleScope, powerJobUser.getId()); - - Set targetIds = Sets.newHashSet(); - role2TargetIds.values().forEach(targetIds::addAll); - // 展示不考虑穿透权限的问题(即拥有 namespace 权限也可以看到全部的 apps) - return targetIds; + return powerJobPermissionService.fetchUserHadPermissionTargets(roleScope, powerJobUser.getId()); } private void diffGrant(RoleScope roleScope, Long target, Role role, List uids, Map> originRole2Uids) { diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AppInfoController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AppInfoController.java index 2f8c6920..97cb7a37 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AppInfoController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AppInfoController.java @@ -1,6 +1,7 @@ package tech.powerjob.server.web.controller; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -109,7 +110,9 @@ public class AppInfoController { Set queryAppIds; Boolean showMyRelated = queryAppInfoRequest.getShowMyRelated(); if (BooleanUtils.isTrue(showMyRelated)) { - queryAppIds = webAuthService.fetchMyPermissionTargets(RoleScope.APP); + Set targetIds = Sets.newHashSet(); + webAuthService.fetchMyPermissionTargets(RoleScope.APP).values().forEach(targetIds::addAll); + queryAppIds = targetIds; } else { queryAppIds = Collections.emptySet(); } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/PwjbUserInfoController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/PwjbUserInfoController.java new file mode 100644 index 00000000..0f846232 --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/PwjbUserInfoController.java @@ -0,0 +1,97 @@ +package tech.powerjob.server.web.controller; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import tech.powerjob.common.response.ResultDTO; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.common.utils.CommonUtils; +import tech.powerjob.server.auth.common.AuthErrorCode; +import tech.powerjob.server.auth.common.PowerJobAuthException; +import tech.powerjob.server.common.utils.DigestUtils; +import tech.powerjob.server.persistence.remote.model.PwjbUserInfoDO; +import tech.powerjob.server.persistence.remote.repository.PwjbUserInfoRepository; +import tech.powerjob.server.web.request.ChangePasswordRequest; +import tech.powerjob.server.web.request.ModifyUserInfoRequest; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.Optional; + +/** + * PowerJob 自带的登录体系 + * (同样视为第三方服务,与主框架没有任何关系) + * + * @author tjq + * @since 2024/2/13 + */ +@RestController +@RequestMapping("/pwjbUser") +public class PwjbUserInfoController { + + @Resource + private PwjbUserInfoRepository pwjbUserInfoRepository; + + /** + * 创建第三方登录体系(PowerJob) 的账户,不允许修改 + * @param request 请求(此处复用了主框架请求,便于用户一次性把所有参数都填入) + * @return 创建结果 + */ + @PostMapping("/create") + public ResultDTO save(@RequestBody ModifyUserInfoRequest request) { + + String username = request.getUsername(); + CommonUtils.requireNonNull(username, "userName can't be null or empty!"); + Optional oldUserOpt = pwjbUserInfoRepository.findByUsername(username); + if (oldUserOpt.isPresent()) { + throw new IllegalArgumentException("username already exist, please change one!"); + } + + PwjbUserInfoDO pwjbUserInfoDO = new PwjbUserInfoDO(); + + pwjbUserInfoDO.setUsername(username); + pwjbUserInfoDO.setGmtCreate(new Date()); + pwjbUserInfoDO.setGmtModified(new Date()); + + // 二次加密密码 + final String password = pwjbUserInfoDO.getPassword(); + if (StringUtils.isNotEmpty(password)) { + pwjbUserInfoDO.setPassword(DigestUtils.rePassword(password, pwjbUserInfoDO.getUsername())); + } + + // 其他参数存入 extra,在回调创建真正的内部 USER 时回填 + request.setPassword(null); + pwjbUserInfoDO.setExtra(JsonUtils.toJSONString(request)); + + pwjbUserInfoRepository.save(pwjbUserInfoDO); + return ResultDTO.success(null); + } + + @PostMapping("/changePassword") + public ResultDTO changePassword(@RequestBody ChangePasswordRequest changePasswordRequest) { + + if (StringUtils.equals(changePasswordRequest.getNewPassword(), changePasswordRequest.getNewPassword2())) { + throw new IllegalArgumentException("Inconsistent passwords"); + } + + Optional userOpt = pwjbUserInfoRepository.findById(changePasswordRequest.getUserId()); + if (!userOpt.isPresent()) { + throw new IllegalArgumentException("can't find user by userId: " + changePasswordRequest.getUserId()); + } + + PwjbUserInfoDO dbUser = userOpt.get(); + String oldPasswordInDb = dbUser.getPassword(); + String oldPasswordInReq = DigestUtils.rePassword(changePasswordRequest.getOldPassword(), dbUser.getUsername()); + if (!StringUtils.equals(oldPasswordInDb, oldPasswordInReq)) { + throw new PowerJobAuthException(AuthErrorCode.INCORRECT_PASSWORD); + } + + dbUser.setPassword(DigestUtils.rePassword(changePasswordRequest.getNewPassword(), dbUser.getUsername())); + dbUser.setGmtModified(new Date()); + pwjbUserInfoRepository.saveAndFlush(dbUser); + + return ResultDTO.success(null); + } +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java index af02f66b..ee2052c4 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java @@ -1,20 +1,44 @@ package tech.powerjob.server.web.controller; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.util.CollectionUtils; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import tech.powerjob.common.response.ResultDTO; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.server.auth.PowerJobUser; +import tech.powerjob.server.auth.Role; +import tech.powerjob.server.auth.RoleScope; +import tech.powerjob.server.auth.common.AuthErrorCode; +import tech.powerjob.server.auth.common.PowerJobAuthException; +import tech.powerjob.server.auth.service.WebAuthService; +import tech.powerjob.server.auth.service.login.PowerJobLoginService; import tech.powerjob.server.core.service.UserService; +import tech.powerjob.server.persistence.remote.model.AppInfoDO; +import tech.powerjob.server.persistence.remote.model.NamespaceDO; import tech.powerjob.server.persistence.remote.model.UserInfoDO; +import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; +import tech.powerjob.server.persistence.remote.repository.NamespaceRepository; import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; +import tech.powerjob.server.web.converter.NamespaceConverter; import tech.powerjob.server.web.converter.UserConverter; -import tech.powerjob.server.web.request.ModifyUserInfoRequest; +import tech.powerjob.server.web.response.AppBaseVO; +import tech.powerjob.server.web.response.NamespaceBaseVO; import tech.powerjob.server.web.response.UserBaseVO; +import tech.powerjob.server.web.response.UserDetailVO; import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; /** @@ -30,16 +54,16 @@ public class UserInfoController { private UserService userService; @Resource private UserInfoRepository userInfoRepository; + @Resource + private PowerJobLoginService powerJobLoginService; + @Resource + private WebAuthService webAuthService; + @Resource + private NamespaceRepository namespaceRepository; + @Resource + private AppInfoRepository appInfoRepository; - @PostMapping("save") - public ResultDTO save(@RequestBody ModifyUserInfoRequest request) { - UserInfoDO userInfoDO = new UserInfoDO(); - BeanUtils.copyProperties(request, userInfoDO); - userService.save(userInfoDO); - return ResultDTO.success(null); - } - - @GetMapping("list") + @GetMapping("/list") public ResultDTO> list(@RequestParam(required = false) String name) { List result; @@ -51,10 +75,76 @@ public class UserInfoController { return ResultDTO.success(convert(result)); } + @GetMapping("/detail") + public ResultDTO getUserDetail(HttpServletRequest httpServletRequest) { + Optional powerJobUserOpt = powerJobLoginService.ifLogin(httpServletRequest); + if (!powerJobUserOpt.isPresent()) { + throw new PowerJobAuthException(AuthErrorCode.USER_NOT_LOGIN); + } + Optional userinfoDoOpt = userInfoRepository.findById(powerJobUserOpt.get().getId()); + if (!userinfoDoOpt.isPresent()) { + throw new IllegalArgumentException("can't find user by id: " + powerJobUserOpt.get().getId()); + } + UserDetailVO userDetailVO = new UserDetailVO(); + BeanUtils.copyProperties(userinfoDoOpt.get(), userDetailVO); + + // 权限信息 + Map> globalPermissions = webAuthService.fetchMyPermissionTargets(RoleScope.GLOBAL); + userDetailVO.setGlobalRoles(globalPermissions.keySet().stream().map(Enum::name).collect(Collectors.toList())); + + Map> namespacePermissions = webAuthService.fetchMyPermissionTargets(RoleScope.NAMESPACE); + List nsList = namespaceRepository.findAllByIdIn(mergeIds(namespacePermissions)); + Map id2NamespaceDo = Maps.newHashMap(); + nsList.forEach(x -> id2NamespaceDo.put(x.getId(), x)); + Map> role2NamespaceBaseVo = Maps.newHashMap(); + namespacePermissions.forEach((k, v) -> { + List namespaceBaseVOS = Lists.newArrayList(); + role2NamespaceBaseVo.put(k.name(), namespaceBaseVOS); + v.forEach(nId -> { + NamespaceDO namespaceDO = id2NamespaceDo.get(nId); + if (namespaceDO == null) { + return; + } + NamespaceBaseVO namespaceBaseVO = JsonUtils.parseObjectIgnoreException(JsonUtils.toJSONString(NamespaceConverter.do2BaseVo(namespaceDO)), NamespaceBaseVO.class); + namespaceBaseVO.genFrontName(); + namespaceBaseVOS.add(namespaceBaseVO); + }); + }); + userDetailVO.setRole2NamespaceList(role2NamespaceBaseVo); + + Map> appPermissions = webAuthService.fetchMyPermissionTargets(RoleScope.APP); + List appList = appInfoRepository.findAllByIdIn(mergeIds(appPermissions)); + Map id2AppInfo = Maps.newHashMap(); + appList.forEach(x -> id2AppInfo.put(x.getId(), x)); + Map> role2AppBaseVo = Maps.newHashMap(); + appPermissions.forEach((k, v) -> { + List appBaseVOS = Lists.newArrayList(); + role2AppBaseVo.put(k.name(), appBaseVOS); + v.forEach(nId -> { + AppInfoDO appInfoDO = id2AppInfo.get(nId); + if (appInfoDO == null) { + return; + } + AppBaseVO appBaseVO = new AppBaseVO(); + BeanUtils.copyProperties(appInfoDO, appBaseVO); + appBaseVOS.add(appBaseVO); + }); + }); + userDetailVO.setRole2AppList(role2AppBaseVo); + + return ResultDTO.success(userDetailVO); + } + private static List convert(List data) { if (CollectionUtils.isEmpty(data)) { return Lists.newLinkedList(); } return data.stream().map(UserConverter::do2BaseVo).collect(Collectors.toList()); } + + private static Set mergeIds(Map> map) { + Set ids = Sets.newHashSet(); + map.values().forEach(ids::addAll); + return ids; + } } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/ChangePasswordRequest.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/ChangePasswordRequest.java new file mode 100644 index 00000000..439fd466 --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/ChangePasswordRequest.java @@ -0,0 +1,23 @@ +package tech.powerjob.server.web.request; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 修改密码 + * + * @author tjq + * @since 2024/2/13 + */ +@Data +public class ChangePasswordRequest implements Serializable { + + private Long userId; + + private String oldPassword; + + private String newPassword; + + private String newPassword2; +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/AppBaseVO.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/AppBaseVO.java new file mode 100644 index 00000000..2c0f778f --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/AppBaseVO.java @@ -0,0 +1,27 @@ +package tech.powerjob.server.web.response; + +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + * AppBaseVO + * + * @author tjq + * @since 2024/2/13 + */ +@Getter +@Setter +public class AppBaseVO implements Serializable { + + protected Long id; + + protected String appName; + + protected Long namespaceId; + /** + * 描述 + */ + protected String title; +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/AppInfoVO.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/AppInfoVO.java index 8315fc99..1fe22c0e 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/AppInfoVO.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/AppInfoVO.java @@ -1,9 +1,10 @@ package tech.powerjob.server.web.response; -import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; import tech.powerjob.server.web.request.ComponentUserRoleInfo; -import java.io.Serializable; import java.util.Date; /** @@ -12,19 +13,10 @@ import java.util.Date; * @author tjq * @since 2024/2/12 */ -@Data -public class AppInfoVO implements Serializable { - - private Long id; - - private String appName; - - private Long namespaceId; - - /** - * 描述 - */ - private String title; +@Getter +@Setter +@ToString +public class AppInfoVO extends AppBaseVO { private String password; diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserBaseVO.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserBaseVO.java index c96458b5..9af769da 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserBaseVO.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserBaseVO.java @@ -14,7 +14,7 @@ import lombok.Setter; @Setter @NoArgsConstructor public class UserBaseVO { - private Long id; - private String username; - private String nick; + protected Long id; + protected String username; + protected String nick; } \ No newline at end of file diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserDetailVO.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserDetailVO.java new file mode 100644 index 00000000..50c72037 --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserDetailVO.java @@ -0,0 +1,60 @@ +package tech.powerjob.server.web.response; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; +import java.util.Map; + +/** + * 用户详细信息 + * + * @author tjq + * @since 2024/2/13 + */ +@Getter +@Setter +@ToString +public class UserDetailVO extends UserBaseVO { + + /** + * 账户类型 + */ + private String accountType; + /** + * 密码 + */ + private String password; + + /** + * 手机号 + */ + private String phone; + /** + * 邮箱地址 + */ + private String email; + /** + * webHook + */ + private String webHook; + /** + * 扩展字段 + */ + private String extra; + + /** + * 拥有的全局权限 + */ + private List globalRoles; + /** + * 拥有的 namespace 权限 + */ + private Map> role2NamespaceList; + /** + * 拥有的 app 权限 + */ + private Map> role2AppList; + +}