trigger-user-save.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. // https://docs.authing.co/v2/reference/sdk-for-node/management/UsersManagementClient.html
  2. const { ManagementClient } = require('authing-node-sdk')
  3. const managementClient = new ManagementClient({
  4. accessKeyId: '669b25e1731d50c59f5494d1',
  5. accessKeySecret: "4cfc095a72a67e22065c97e90054315c",
  6. host: 'https://textbook.u2-dev.hep.com.cn', // 应用的认证地址
  7. })
  8. /**
  9. * 用户创建前,创建用户至Authing
  10. * @desc 仅同步注册信息及密码,资料在afterSave中同步
  11. * @example
  12. * 注册:
  13. curl -X POST -H "Content-Type: application/json" -H "X-Parse-Application-Id: edu-textbook" -d '{
  14. "username": "333",
  15. "password": "333"
  16. }' http://127.0.0.1:61337/parse/users
  17. * 删除:
  18. */
  19. export function defineUserBeforeSave(){
  20. Parse.Cloud.beforeSave("_User", async (request) => {
  21. // console.log("UserBeforeSave")
  22. request.object = appendUserACL(request.object)
  23. let user = request.object;
  24. // 仅首次注册/创建用户/修改密码有password属性,同步Authing账号
  25. let password = user?.get("password");
  26. // console.log(password)
  27. if(password){
  28. let mobile = user?.get("mobile");
  29. let email = user?.get("email");
  30. let username = user?.get("username");
  31. let externalId = user?.id;
  32. let authingUserExists = await findUserByMobileEmailUserName(user)
  33. // console.log(authingUserExists)
  34. if(authingUserExists?.statusCode==404){ // 创建用户
  35. let newuser = {
  36. status:"Activated",
  37. password:password,
  38. }
  39. if(mobile){newuser.phone = mobile}
  40. if(email){newuser.email = email}
  41. if(username){newuser.username = username}
  42. if(externalId){newuser.externalId = externalId}
  43. // console.log(newuser)
  44. result = await managementClient.createUser(newuser)
  45. }
  46. if(authingUserExists?.statusCode==200){
  47. let existsUser = authingUserExists?.data;
  48. try{
  49. let updateRes = await managementClient.updateUser({
  50. userId:existsUser?.userId,
  51. password:password,
  52. })
  53. // console.log(result)
  54. }catch(err){console.log(err)}
  55. }
  56. }
  57. });
  58. }
  59. function appendUserACL(user){
  60. let acl = user?.getACL();
  61. if(!acl) acl = new Parse.ACL();
  62. // 添加superadmin用户的可读可写权限
  63. acl.setRoleWriteAccess("superadmin", true);
  64. acl.setRoleReadAccess("superadmin", true);
  65. // 添加admin用户的可读可写权限
  66. acl.setRoleWriteAccess("admin", true);
  67. acl.setRoleReadAccess("admin", true);
  68. // 添加manager用户的可读可写权限
  69. acl.setRoleWriteAccess("manager", true);
  70. acl.setRoleReadAccess("manager", true);
  71. // 添加公共可读权限
  72. acl.setPublicReadAccess(true);
  73. user.setACL(acl);
  74. return user
  75. }
  76. /**
  77. * 用户删除前,删除用户从Authing
  78. */
  79. export function defineUserAfterDelete(){
  80. Parse.Cloud.afterDelete("_User", async (request) => {
  81. let user = request.object;
  82. // console.log(user.toJSON());
  83. // console.log(user?.get("password"))
  84. try{
  85. let authingUserExists = await findUserByMobileEmailUserName(user)
  86. if(authingUserExists?.statusCode==200){
  87. let existsUser = authingUserExists?.data;
  88. let deleteRes = await managementClient.deleteUsersBatch({
  89. // 替换用户 ID 数组
  90. userIds: [existsUser.userId],
  91. options: {
  92. userIdType: "user_id"
  93. }
  94. });
  95. }
  96. }catch(deleteErr){}
  97. // 直接给予删除权限
  98. // if(request?.master){
  99. // return await object.destroy({useMasterKey:true})
  100. // }
  101. // let requestUser = request.user;
  102. // if(!requestUser?.id){
  103. // throw new Parse.Error(206,"Insufficient auth.")
  104. // }
  105. // let query = new Parse.Query("_Role");
  106. // query.equalTo("users",requestUser)
  107. // let roles = await query.find({useMasterKey:true});
  108. // let acl = request.object.getACL();
  109. // if(!acl){
  110. // throw new Parse.Error(206,"Object has no ACL.")
  111. // }
  112. // if(acl.getWriteAccess(requestUser)){
  113. // return await request.object.destroy({useMasterKey:true})
  114. // }else{
  115. // let hasPermission = roles.some(role=>acl.getRoleWriteAccess(role.getName()))
  116. // if(hasPermission) {
  117. // return await request.object.destroy({useMasterKey:true})
  118. // }
  119. // throw new Parse.Error(206,"Insufficient auth.")
  120. // }
  121. });
  122. }
  123. /**
  124. * 用户保存后同步数据至Authing
  125. */
  126. export function defineUserAfterSave(){
  127. Parse.Cloud.afterSave("_User", async (request) => {
  128. // console.log("save _User",request?.object?.id)
  129. let query = new Parse.Query("Profile");
  130. query.equalTo("user",request?.object?.id)
  131. let profile = await query.first();
  132. syncUserProfileToAuthing(request?.object,profile)
  133. });
  134. Parse.Cloud.afterSave("Profile", async (request) => {
  135. // console.log("save Profile",request?.object?.id)
  136. let query = new Parse.Query("Profile");
  137. let userPointer = request?.object?.get("user")
  138. query.include("user");
  139. // console.log(userPointer)
  140. profile = await query.get(request?.object?.id,{useMasterKey:true});
  141. syncUserProfileToAuthing(profile.get("user"),profile)
  142. appandUserToRole(profile.get("user")?.toPointer() || userPointer,profile?.get("identity"))
  143. });
  144. }
  145. // 同步Profile角色身份
  146. var roleNameMap = {
  147. "国家级管理员":"superadmin",
  148. "工作联系人":"admin",
  149. "高校联系人":"manager",
  150. }
  151. async function appandUserToRole(user,roleName) {
  152. // console.log(user,roleName)
  153. roleName = roleNameMap[roleName] || roleName
  154. let id = user?.id || user?.objectId
  155. if(!id || !roleName) return
  156. let userObj = new Parse.User();
  157. userObj.id = id;
  158. let query = new Parse.Query(Parse.Role);
  159. query.equalTo("name", roleName);
  160. try{
  161. let role = await query.first({ useMasterKey: true });
  162. // console.log(role?.toJSON())
  163. if (role?.toJSON()?.name) {
  164. let usersRelation = role.relation("users");
  165. usersRelation.add(userObj);
  166. await role.save(null, { useMasterKey: true });
  167. let users = await usersRelation.query().find({ useMasterKey: true });
  168. // console.log('Users in role after addition:', users.map(u => u.toJSON()));
  169. }
  170. }catch(err){
  171. console.error(err)
  172. }
  173. }
  174. /**
  175. * 查询用户
  176. * @param {*} user
  177. * @returns
  178. * 不存在:
  179. * {
  180. statusCode: 404,
  181. apiCode: 2004,
  182. message: '用户不存在',
  183. requestId: 'e59cb407-f2d9-4bd8-ac93-d2dbc8d6ab72'
  184. }
  185. {
  186. statusCode: 200,
  187. message: '',
  188. data: {
  189. userId: '669676e5a3a9ac870bfff2a3',
  190. createdAt: '2024-07-16T13:34:29.789Z',
  191. updatedAt: '2024-07-16T13:34:29.789Z',
  192. status: 'Activated'
  193. }
  194. }
  195. */
  196. async function findUserByMobileEmailUserName(user){
  197. let mobile = user?.get("mobile");
  198. let email = user?.get("email");
  199. let username = user?.get("username");
  200. let externalId = user?.id;
  201. let result;
  202. if(email){
  203. try{
  204. result = await managementClient.getUser({userIdType:`email`,userId:email})
  205. // console.log(email,result)
  206. }catch(err){}
  207. }
  208. if(mobile&&(!result || result?.statusCode==404)){
  209. try{
  210. result = await managementClient.getUser({userIdType:`phone`,userId:mobile})
  211. // console.log(mobile,result)
  212. }catch(err){}
  213. }
  214. if(username&&(!result || result?.statusCode==404)){
  215. try{
  216. result = await managementClient.getUser({userIdType:`username`,userId:username})
  217. // console.log("username",username,result)
  218. }catch(err){}
  219. }
  220. if(externalId&&(!result || result?.statusCode==404)){
  221. try{
  222. result = await managementClient.getUser({userIdType:`external_id`,userId:externalId})
  223. // console.log(externalId,result)
  224. }catch(err){}
  225. }
  226. return result
  227. }
  228. async function syncUserProfileToAuthing(user,profile){
  229. if(!user?.id) return
  230. let userInfo = user.toJSON();
  231. userInfo = fixJsonFileds(userInfo)
  232. if(profile?.id){
  233. let pjson = profile.toJSON();
  234. delete pjson.objectId;
  235. pjson= fixJsonFileds(pjson)
  236. Object.keys(pjson).forEach(key=>{
  237. userInfo[key] = pjson[key]
  238. })
  239. }
  240. // 映射对应字段
  241. userInfo.company = userInfo.companyName
  242. delete userInfo.companyName
  243. userInfo.userType = userInfo.identity
  244. delete userInfo.identity
  245. userInfo.userId = userInfo.objectId
  246. let authingUserExists = await findUserByMobileEmailUserName(user)
  247. if(authingUserExists?.statusCode==200){
  248. let existsUser = authingUserExists?.data;
  249. userInfo.userId = existsUser.userId
  250. }
  251. delete userInfo.objectId
  252. userInfo.identifyStatus = userInfo.accountState
  253. // 自定义数据全量同步
  254. userInfo.customData = JSON.parse(JSON.stringify(userInfo))
  255. // {
  256. // identifyStatus : userInfo.accountState
  257. // }
  258. // console.log(userInfo)
  259. // 同步数据至Authing用户池
  260. let result
  261. try{
  262. result = await managementClient.updateUser(userInfo)
  263. // console.log(result)
  264. }catch(err){console.log(err)}
  265. }
  266. function fixJsonFileds(json){
  267. // Parse独有关系数据
  268. delete json.ACL
  269. delete json.className
  270. delete json.sessionToken
  271. delete json.company
  272. delete json.user
  273. delete json.createdAt
  274. delete json.updatedAt
  275. // 来自Authing的数据
  276. delete json?.loginsCount
  277. delete json?.lastIP
  278. delete json?.lastLogin
  279. return json
  280. }