permserver_test.go 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204
  1. package server
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "testing"
  7. "time"
  8. authHelper "perms-system-server/internal/logic/auth"
  9. permModel "perms-system-server/internal/model/perm"
  10. productModel "perms-system-server/internal/model/product"
  11. memberModel "perms-system-server/internal/model/productmember"
  12. roleModel "perms-system-server/internal/model/role"
  13. rolePermModel "perms-system-server/internal/model/roleperm"
  14. userModel "perms-system-server/internal/model/user"
  15. userPermModel "perms-system-server/internal/model/userperm"
  16. userRoleModel "perms-system-server/internal/model/userrole"
  17. "perms-system-server/internal/svc"
  18. "perms-system-server/internal/testutil"
  19. "perms-system-server/pb"
  20. "github.com/golang-jwt/jwt/v4"
  21. "github.com/stretchr/testify/assert"
  22. "github.com/stretchr/testify/require"
  23. "golang.org/x/crypto/bcrypt"
  24. "google.golang.org/grpc/codes"
  25. "google.golang.org/grpc/status"
  26. )
  27. func bcryptHash(t *testing.T, plaintext string) string {
  28. t.Helper()
  29. h, err := bcrypt.GenerateFromPassword([]byte(plaintext), bcrypt.MinCost)
  30. require.NoError(t, err)
  31. return string(h)
  32. }
  33. // ---------- SyncPermissions ----------
  34. // TC-0195: 正常同步
  35. func TestSyncPermissions_Normal(t *testing.T) {
  36. ctx := context.Background()
  37. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  38. conn := testutil.GetTestSqlConn()
  39. now := time.Now().Unix()
  40. uid := testutil.UniqueId()
  41. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  42. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
  43. Status: 1, CreateTime: now, UpdateTime: now,
  44. })
  45. require.NoError(t, err)
  46. pId, _ := pRes.LastInsertId()
  47. t.Cleanup(func() {
  48. testutil.CleanTableByField(ctx, conn, "`sys_perm`", "productCode", uid)
  49. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  50. })
  51. srv := NewPermServer(svcCtx)
  52. resp, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  53. AppKey: uid,
  54. AppSecret: "secret1",
  55. Perms: []*pb.PermItem{
  56. {Code: "perm_a", Name: "Perm A", Remark: "remark_a"},
  57. {Code: "perm_b", Name: "Perm B", Remark: "remark_b"},
  58. },
  59. })
  60. require.NoError(t, err)
  61. assert.Equal(t, int64(2), resp.Added)
  62. assert.Equal(t, int64(0), resp.Updated)
  63. assert.Equal(t, int64(0), resp.Disabled)
  64. resp2, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  65. AppKey: uid,
  66. AppSecret: "secret1",
  67. Perms: []*pb.PermItem{
  68. {Code: "perm_a", Name: "Perm A Updated", Remark: "remark_a"},
  69. },
  70. })
  71. require.NoError(t, err)
  72. assert.Equal(t, int64(0), resp2.Added)
  73. assert.Equal(t, int64(1), resp2.Updated)
  74. assert.Equal(t, int64(1), resp2.Disabled)
  75. }
  76. // TC-0196: appKey无效
  77. func TestSyncPermissions_InvalidAppKey(t *testing.T) {
  78. ctx := context.Background()
  79. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  80. srv := NewPermServer(svcCtx)
  81. _, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  82. AppKey: "nonexistent_key",
  83. AppSecret: "any",
  84. Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
  85. })
  86. require.Error(t, err)
  87. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  88. assert.Equal(t, "无效的appKey", status.Convert(err).Message())
  89. }
  90. // TC-0197: appSecret错误
  91. func TestSyncPermissions_WrongAppSecret(t *testing.T) {
  92. ctx := context.Background()
  93. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  94. conn := testutil.GetTestSqlConn()
  95. now := time.Now().Unix()
  96. uid := testutil.UniqueId()
  97. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  98. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "real_secret"),
  99. Status: 1, CreateTime: now, UpdateTime: now,
  100. })
  101. require.NoError(t, err)
  102. pId, _ := pRes.LastInsertId()
  103. t.Cleanup(func() {
  104. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  105. })
  106. srv := NewPermServer(svcCtx)
  107. _, err = srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  108. AppKey: uid,
  109. AppSecret: "wrong_secret",
  110. Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
  111. })
  112. require.Error(t, err)
  113. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  114. assert.Equal(t, "appSecret验证失败", status.Convert(err).Message())
  115. }
  116. // TC-0198: 产品已禁用
  117. func TestSyncPermissions_ProductDisabled(t *testing.T) {
  118. ctx := context.Background()
  119. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  120. conn := testutil.GetTestSqlConn()
  121. now := time.Now().Unix()
  122. uid := testutil.UniqueId()
  123. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  124. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
  125. Status: 2, CreateTime: now, UpdateTime: now,
  126. })
  127. require.NoError(t, err)
  128. pId, _ := pRes.LastInsertId()
  129. t.Cleanup(func() {
  130. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  131. })
  132. srv := NewPermServer(svcCtx)
  133. _, err = srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  134. AppKey: uid,
  135. AppSecret: "secret1",
  136. Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
  137. })
  138. require.Error(t, err)
  139. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  140. assert.Equal(t, "产品已被禁用", status.Convert(err).Message())
  141. }
  142. // ---------- Login ----------
  143. // TC-0200: 正常登录(普通用户+productCode)
  144. func TestLogin_Normal(t *testing.T) {
  145. ctx := context.Background()
  146. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  147. conn := testutil.GetTestSqlConn()
  148. now := time.Now().Unix()
  149. uid := testutil.UniqueId()
  150. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  151. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  152. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  153. Status: 1, CreateTime: now, UpdateTime: now,
  154. })
  155. require.NoError(t, err)
  156. uId, _ := uRes.LastInsertId()
  157. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  158. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  159. Status: 1, CreateTime: now, UpdateTime: now,
  160. })
  161. require.NoError(t, err)
  162. pId, _ := pRes.LastInsertId()
  163. pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  164. ProductCode: uid, UserId: uId, MemberType: "MEMBER", Status: 1,
  165. CreateTime: now, UpdateTime: now,
  166. })
  167. require.NoError(t, err)
  168. pmId, _ := pmRes.LastInsertId()
  169. t.Cleanup(func() {
  170. testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId)
  171. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  172. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  173. })
  174. srv := NewPermServer(svcCtx)
  175. resp, err := srv.Login(ctx, &pb.LoginReq{
  176. Username: uid,
  177. Password: "pass123",
  178. ProductCode: uid,
  179. })
  180. require.NoError(t, err)
  181. assert.NotEmpty(t, resp.AccessToken)
  182. assert.NotEmpty(t, resp.RefreshToken)
  183. assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
  184. assert.Equal(t, uId, resp.UserId)
  185. assert.Equal(t, uid, resp.Username)
  186. // BUG-01: proto定义了nickname字段,实现应返回用户昵称
  187. assert.Equal(t, "nick", resp.Nickname, "BUG-01: LoginResp.Nickname 应返回用户昵称而非空字符串")
  188. }
  189. // TC-0201: 用户不存在
  190. func TestLogin_UserNotFound(t *testing.T) {
  191. ctx := context.Background()
  192. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  193. srv := NewPermServer(svcCtx)
  194. _, err := srv.Login(ctx, &pb.LoginReq{
  195. Username: "nonexistent_user_xyz",
  196. Password: "any",
  197. ProductCode: "any_product",
  198. })
  199. require.Error(t, err)
  200. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  201. assert.Equal(t, "用户名或密码错误", status.Convert(err).Message())
  202. }
  203. // TC-0202: 密码错误
  204. func TestLogin_WrongPassword(t *testing.T) {
  205. ctx := context.Background()
  206. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  207. conn := testutil.GetTestSqlConn()
  208. now := time.Now().Unix()
  209. uid := testutil.UniqueId()
  210. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  211. Username: uid, Password: testutil.HashPassword("correct_pass"), Nickname: "nick",
  212. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  213. Status: 1, CreateTime: now, UpdateTime: now,
  214. })
  215. require.NoError(t, err)
  216. uId, _ := uRes.LastInsertId()
  217. t.Cleanup(func() {
  218. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  219. })
  220. srv := NewPermServer(svcCtx)
  221. _, err = srv.Login(ctx, &pb.LoginReq{
  222. Username: uid,
  223. Password: "wrong_pass",
  224. ProductCode: "any_product",
  225. })
  226. require.Error(t, err)
  227. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  228. assert.Equal(t, "用户名或密码错误", status.Convert(err).Message())
  229. }
  230. // TC-0203: 账号冻结
  231. func TestLogin_AccountFrozen(t *testing.T) {
  232. ctx := context.Background()
  233. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  234. conn := testutil.GetTestSqlConn()
  235. now := time.Now().Unix()
  236. uid := testutil.UniqueId()
  237. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  238. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  239. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  240. Status: 2, CreateTime: now, UpdateTime: now,
  241. })
  242. require.NoError(t, err)
  243. uId, _ := uRes.LastInsertId()
  244. t.Cleanup(func() {
  245. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  246. })
  247. srv := NewPermServer(svcCtx)
  248. _, err = srv.Login(ctx, &pb.LoginReq{
  249. Username: uid,
  250. Password: "pass123",
  251. ProductCode: "any_product",
  252. })
  253. require.Error(t, err)
  254. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  255. assert.Equal(t, "账号已被冻结", status.Convert(err).Message())
  256. }
  257. // TC-0204: 超管被拒绝
  258. func TestLogin_SuperAdminRejected(t *testing.T) {
  259. ctx := context.Background()
  260. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  261. conn := testutil.GetTestSqlConn()
  262. now := time.Now().Unix()
  263. uid := testutil.UniqueId()
  264. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  265. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "sa",
  266. Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
  267. Status: 1, CreateTime: now, UpdateTime: now,
  268. })
  269. require.NoError(t, err)
  270. uId, _ := uRes.LastInsertId()
  271. t.Cleanup(func() {
  272. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  273. })
  274. srv := NewPermServer(svcCtx)
  275. _, err = srv.Login(ctx, &pb.LoginReq{
  276. Username: uid,
  277. Password: "pass123",
  278. ProductCode: "any_product",
  279. })
  280. require.Error(t, err)
  281. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  282. assert.Equal(t, "超级管理员不允许通过产品端登录,请使用管理后台", status.Convert(err).Message())
  283. }
  284. // TC-0205: 普通用户+productCode
  285. func TestLogin_NormalUserWithProductCode(t *testing.T) {
  286. ctx := context.Background()
  287. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  288. conn := testutil.GetTestSqlConn()
  289. now := time.Now().Unix()
  290. uid := testutil.UniqueId()
  291. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  292. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  293. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  294. Status: 1, CreateTime: now, UpdateTime: now,
  295. })
  296. require.NoError(t, err)
  297. uId, _ := uRes.LastInsertId()
  298. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  299. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  300. Status: 1, CreateTime: now, UpdateTime: now,
  301. })
  302. require.NoError(t, err)
  303. pId, _ := pRes.LastInsertId()
  304. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  305. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  306. Status: 1, CreateTime: now, UpdateTime: now,
  307. })
  308. require.NoError(t, err)
  309. mbrId, _ := mbrRes.LastInsertId()
  310. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  311. ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
  312. CreateTime: now, UpdateTime: now,
  313. })
  314. require.NoError(t, err)
  315. roleId, _ := roleRes.LastInsertId()
  316. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  317. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  318. Status: 1, CreateTime: now, UpdateTime: now,
  319. })
  320. require.NoError(t, err)
  321. pm1Id, _ := pm1Res.LastInsertId()
  322. urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
  323. UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
  324. })
  325. require.NoError(t, err)
  326. urId, _ := urRes.LastInsertId()
  327. rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  328. RoleId: roleId, PermId: pm1Id, CreateTime: now, UpdateTime: now,
  329. })
  330. require.NoError(t, err)
  331. rpId, _ := rpRes.LastInsertId()
  332. t.Cleanup(func() {
  333. testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId)
  334. testutil.CleanTable(ctx, conn, "`sys_user_role`", urId)
  335. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  336. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  337. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  338. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  339. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  340. })
  341. srv := NewPermServer(svcCtx)
  342. resp, err := srv.Login(ctx, &pb.LoginReq{
  343. Username: uid,
  344. Password: "pass123",
  345. ProductCode: uid,
  346. })
  347. require.NoError(t, err)
  348. assert.Equal(t, "MEMBER", resp.MemberType)
  349. assert.Contains(t, resp.Perms, uid+"_c1")
  350. assert.NotEmpty(t, resp.AccessToken)
  351. assert.NotEmpty(t, resp.RefreshToken)
  352. }
  353. // TC-0207: productCode为空
  354. func TestLogin_EmptyProductCode(t *testing.T) {
  355. ctx := context.Background()
  356. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  357. srv := NewPermServer(svcCtx)
  358. _, err := srv.Login(ctx, &pb.LoginReq{
  359. Username: "anyuser",
  360. Password: "anypass",
  361. ProductCode: "",
  362. })
  363. require.Error(t, err)
  364. assert.Equal(t, codes.InvalidArgument, status.Code(err))
  365. assert.Equal(t, "productCode不能为空", status.Convert(err).Message())
  366. }
  367. // ---------- RefreshToken ----------
  368. // TC-0208: 正常刷新(refreshToken原样返回,不重新生成)
  369. func TestRefreshToken_Normal(t *testing.T) {
  370. ctx := context.Background()
  371. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  372. conn := testutil.GetTestSqlConn()
  373. now := time.Now().Unix()
  374. uid := testutil.UniqueId()
  375. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  376. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  377. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  378. Status: 1, CreateTime: now, UpdateTime: now,
  379. })
  380. require.NoError(t, err)
  381. uId, _ := uRes.LastInsertId()
  382. t.Cleanup(func() {
  383. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  384. })
  385. cfg := testutil.GetTestConfig()
  386. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, "", 0)
  387. require.NoError(t, err)
  388. srv := NewPermServer(svcCtx)
  389. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  390. RefreshToken: refreshToken,
  391. })
  392. require.NoError(t, err)
  393. assert.NotEmpty(t, resp.AccessToken)
  394. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回,不重新生成")
  395. assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
  396. }
  397. // TC-0209: token无效
  398. func TestRefreshToken_InvalidToken(t *testing.T) {
  399. ctx := context.Background()
  400. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  401. srv := NewPermServer(svcCtx)
  402. _, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  403. RefreshToken: "invalid.token.string",
  404. })
  405. require.Error(t, err)
  406. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  407. assert.Equal(t, "refreshToken无效或已过期", status.Convert(err).Message())
  408. }
  409. // TC-0210: 账号冻结
  410. func TestRefreshToken_AccountFrozen(t *testing.T) {
  411. ctx := context.Background()
  412. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  413. conn := testutil.GetTestSqlConn()
  414. now := time.Now().Unix()
  415. uid := testutil.UniqueId()
  416. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  417. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  418. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  419. Status: 2, CreateTime: now, UpdateTime: now,
  420. })
  421. require.NoError(t, err)
  422. uId, _ := uRes.LastInsertId()
  423. t.Cleanup(func() {
  424. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  425. })
  426. cfg := testutil.GetTestConfig()
  427. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, "", 0)
  428. require.NoError(t, err)
  429. srv := NewPermServer(svcCtx)
  430. _, err = srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  431. RefreshToken: refreshToken,
  432. })
  433. require.Error(t, err)
  434. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  435. assert.Equal(t, "账号已被冻结", status.Convert(err).Message())
  436. }
  437. // TC-0211: productCode回退到claims
  438. func TestRefreshToken_FallbackToClaimsProductCode(t *testing.T) {
  439. ctx := context.Background()
  440. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  441. conn := testutil.GetTestSqlConn()
  442. now := time.Now().Unix()
  443. uid := testutil.UniqueId()
  444. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  445. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  446. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  447. Status: 1, CreateTime: now, UpdateTime: now,
  448. })
  449. require.NoError(t, err)
  450. uId, _ := uRes.LastInsertId()
  451. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  452. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  453. Status: 1, CreateTime: now, UpdateTime: now,
  454. })
  455. require.NoError(t, err)
  456. pId, _ := pRes.LastInsertId()
  457. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  458. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  459. Status: 1, CreateTime: now, UpdateTime: now,
  460. })
  461. require.NoError(t, err)
  462. mbrId, _ := mbrRes.LastInsertId()
  463. t.Cleanup(func() {
  464. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  465. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  466. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  467. })
  468. cfg := testutil.GetTestConfig()
  469. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid, 0)
  470. require.NoError(t, err)
  471. srv := NewPermServer(svcCtx)
  472. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  473. RefreshToken: refreshToken,
  474. ProductCode: "",
  475. })
  476. require.NoError(t, err)
  477. assert.NotEmpty(t, resp.AccessToken)
  478. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回")
  479. }
  480. // TC-0212: 超管+productCode
  481. func TestRefreshToken_SuperAdminWithProductCode(t *testing.T) {
  482. ctx := context.Background()
  483. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  484. conn := testutil.GetTestSqlConn()
  485. now := time.Now().Unix()
  486. uid := testutil.UniqueId()
  487. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  488. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "sa",
  489. Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
  490. Status: 1, CreateTime: now, UpdateTime: now,
  491. })
  492. require.NoError(t, err)
  493. uId, _ := uRes.LastInsertId()
  494. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  495. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  496. Status: 1, CreateTime: now, UpdateTime: now,
  497. })
  498. require.NoError(t, err)
  499. pId, _ := pRes.LastInsertId()
  500. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  501. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  502. Status: 1, CreateTime: now, UpdateTime: now,
  503. })
  504. require.NoError(t, err)
  505. pm1Id, _ := pm1Res.LastInsertId()
  506. t.Cleanup(func() {
  507. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  508. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  509. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  510. })
  511. cfg := testutil.GetTestConfig()
  512. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid, 0)
  513. require.NoError(t, err)
  514. srv := NewPermServer(svcCtx)
  515. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  516. RefreshToken: refreshToken,
  517. ProductCode: uid,
  518. })
  519. require.NoError(t, err)
  520. assert.NotEmpty(t, resp.AccessToken)
  521. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回")
  522. }
  523. // TC-0213: 普通用户+productCode
  524. func TestRefreshToken_NormalUserWithProductCode(t *testing.T) {
  525. ctx := context.Background()
  526. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  527. conn := testutil.GetTestSqlConn()
  528. now := time.Now().Unix()
  529. uid := testutil.UniqueId()
  530. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  531. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  532. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  533. Status: 1, CreateTime: now, UpdateTime: now,
  534. })
  535. require.NoError(t, err)
  536. uId, _ := uRes.LastInsertId()
  537. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  538. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  539. Status: 1, CreateTime: now, UpdateTime: now,
  540. })
  541. require.NoError(t, err)
  542. pId, _ := pRes.LastInsertId()
  543. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  544. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  545. Status: 1, CreateTime: now, UpdateTime: now,
  546. })
  547. require.NoError(t, err)
  548. mbrId, _ := mbrRes.LastInsertId()
  549. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  550. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  551. Status: 1, CreateTime: now, UpdateTime: now,
  552. })
  553. require.NoError(t, err)
  554. pm1Id, _ := pm1Res.LastInsertId()
  555. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  556. ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
  557. CreateTime: now, UpdateTime: now,
  558. })
  559. require.NoError(t, err)
  560. roleId, _ := roleRes.LastInsertId()
  561. urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
  562. UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
  563. })
  564. require.NoError(t, err)
  565. urId, _ := urRes.LastInsertId()
  566. rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  567. RoleId: roleId, PermId: pm1Id, CreateTime: now, UpdateTime: now,
  568. })
  569. require.NoError(t, err)
  570. rpId, _ := rpRes.LastInsertId()
  571. t.Cleanup(func() {
  572. testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId)
  573. testutil.CleanTable(ctx, conn, "`sys_user_role`", urId)
  574. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  575. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  576. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  577. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  578. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  579. })
  580. cfg := testutil.GetTestConfig()
  581. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid, 0)
  582. require.NoError(t, err)
  583. srv := NewPermServer(svcCtx)
  584. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  585. RefreshToken: refreshToken,
  586. ProductCode: uid,
  587. })
  588. require.NoError(t, err)
  589. assert.NotEmpty(t, resp.AccessToken)
  590. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回")
  591. }
  592. // ---------- VerifyToken ----------
  593. // TC-0214: 有效token(VerifyToken 现在实时查询DB,需要真实数据)
  594. func TestVerifyToken_Valid(t *testing.T) {
  595. ctx := context.Background()
  596. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  597. cfg := testutil.GetTestConfig()
  598. conn := testutil.GetTestSqlConn()
  599. ts := time.Now().Unix()
  600. uid := testutil.UniqueId()
  601. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  602. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick_verify",
  603. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  604. Status: 1, CreateTime: ts, UpdateTime: ts,
  605. })
  606. require.NoError(t, err)
  607. uId, _ := uRes.LastInsertId()
  608. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  609. Code: uid, Name: "prod_verify", AppKey: uid + "_k", AppSecret: "s1",
  610. Status: 1, CreateTime: ts, UpdateTime: ts,
  611. })
  612. require.NoError(t, err)
  613. pId, _ := pRes.LastInsertId()
  614. pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  615. ProductCode: uid, UserId: uId, MemberType: "ADMIN", Status: 1,
  616. CreateTime: ts, UpdateTime: ts,
  617. })
  618. require.NoError(t, err)
  619. pmId, _ := pmRes.LastInsertId()
  620. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  621. ProductCode: uid, Name: "perm_a", Code: "perm_a",
  622. Status: 1, CreateTime: ts, UpdateTime: ts,
  623. })
  624. require.NoError(t, err)
  625. pm1Id, _ := pm1Res.LastInsertId()
  626. pm2Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  627. ProductCode: uid, Name: "perm_b", Code: "perm_b",
  628. Status: 1, CreateTime: ts, UpdateTime: ts,
  629. })
  630. require.NoError(t, err)
  631. pm2Id, _ := pm2Res.LastInsertId()
  632. t.Cleanup(func() {
  633. svcCtx.UserDetailsLoader.Del(ctx, uId, uid)
  634. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id, pm2Id)
  635. testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId)
  636. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  637. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  638. })
  639. svcCtx.UserDetailsLoader.Del(ctx, uId, uid)
  640. accessToken, err := authHelper.GenerateAccessToken(
  641. cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
  642. uId, uid, uid, "ADMIN", 0,
  643. )
  644. require.NoError(t, err)
  645. srv := NewPermServer(svcCtx)
  646. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
  647. require.NoError(t, err)
  648. assert.True(t, resp.Valid)
  649. assert.Equal(t, uId, resp.UserId)
  650. assert.Equal(t, uid, resp.Username)
  651. assert.Equal(t, "ADMIN", resp.MemberType)
  652. assert.ElementsMatch(t, []string{"perm_a", "perm_b"}, resp.Perms)
  653. // BUG-02: proto定义了productCode字段,实现应返回产品编码
  654. assert.Equal(t, uid, resp.ProductCode, "BUG-02: VerifyTokenResp.ProductCode 应返回产品编码而非空字符串")
  655. }
  656. // TC-0215: 无效token
  657. func TestVerifyToken_Invalid(t *testing.T) {
  658. ctx := context.Background()
  659. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  660. srv := NewPermServer(svcCtx)
  661. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: "invalid.token.here"})
  662. require.NoError(t, err)
  663. assert.False(t, resp.Valid)
  664. }
  665. // TC-0216: 缺少userId
  666. func TestVerifyToken_MissingUserId(t *testing.T) {
  667. ctx := context.Background()
  668. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  669. cfg := testutil.GetTestConfig()
  670. // Generate a token without userId by using raw JWT
  671. token := createTokenWithoutUserId(cfg.Auth.AccessSecret)
  672. srv := NewPermServer(svcCtx)
  673. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: token})
  674. require.NoError(t, err)
  675. assert.False(t, resp.Valid)
  676. }
  677. // ---------- GetUserPerms ----------
  678. // TC-0220: 用户不存在
  679. func TestGetUserPerms_UserNotFound(t *testing.T) {
  680. ctx := context.Background()
  681. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  682. conn := testutil.GetTestSqlConn()
  683. now := time.Now().Unix()
  684. uid := testutil.UniqueId()
  685. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  686. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
  687. Status: 1, CreateTime: now, UpdateTime: now,
  688. })
  689. require.NoError(t, err)
  690. pId, _ := pRes.LastInsertId()
  691. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product`", pId) })
  692. srv := NewPermServer(svcCtx)
  693. _, err = srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
  694. UserId: 999999999,
  695. ProductCode: uid,
  696. AppKey: uid,
  697. AppSecret: "secret1",
  698. })
  699. require.Error(t, err)
  700. assert.Equal(t, codes.NotFound, status.Code(err))
  701. assert.Equal(t, "用户不存在", status.Convert(err).Message())
  702. }
  703. // TC-0221: 超管
  704. func TestGetUserPerms_SuperAdmin(t *testing.T) {
  705. ctx := context.Background()
  706. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  707. conn := testutil.GetTestSqlConn()
  708. now := time.Now().Unix()
  709. uid := testutil.UniqueId()
  710. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  711. Username: uid, Password: testutil.HashPassword("pass"), Nickname: "sa",
  712. Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
  713. Status: 1, CreateTime: now, UpdateTime: now,
  714. })
  715. require.NoError(t, err)
  716. uId, _ := uRes.LastInsertId()
  717. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  718. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
  719. Status: 1, CreateTime: now, UpdateTime: now,
  720. })
  721. require.NoError(t, err)
  722. pId, _ := pRes.LastInsertId()
  723. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  724. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  725. Status: 1, CreateTime: now, UpdateTime: now,
  726. })
  727. require.NoError(t, err)
  728. pm1Id, _ := pm1Res.LastInsertId()
  729. mRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  730. ProductCode: uid, UserId: uId, MemberType: "ADMIN",
  731. Status: 1, CreateTime: now, UpdateTime: now,
  732. })
  733. require.NoError(t, err)
  734. mId, _ := mRes.LastInsertId()
  735. t.Cleanup(func() {
  736. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  737. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  738. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  739. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  740. })
  741. srv := NewPermServer(svcCtx)
  742. resp, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
  743. UserId: uId,
  744. ProductCode: uid,
  745. AppKey: uid,
  746. AppSecret: "secret1",
  747. })
  748. require.NoError(t, err)
  749. assert.Equal(t, "SUPER_ADMIN", resp.MemberType)
  750. assert.Contains(t, resp.Perms, uid+"_c1")
  751. }
  752. // TC-0199: 验证disabled计数
  753. func TestSyncPermissions_VerifyDisabledCount(t *testing.T) {
  754. ctx := context.Background()
  755. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  756. conn := testutil.GetTestSqlConn()
  757. now := time.Now().Unix()
  758. uid := testutil.UniqueId()
  759. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  760. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
  761. Status: 1, CreateTime: now, UpdateTime: now,
  762. })
  763. require.NoError(t, err)
  764. pId, _ := pRes.LastInsertId()
  765. var permIds []int64
  766. for i := 0; i < 5; i++ {
  767. pmRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  768. ProductCode: uid, Name: "p", Code: fmt.Sprintf("%s_c%d", uid, i),
  769. Status: 1, CreateTime: now, UpdateTime: now,
  770. })
  771. require.NoError(t, err)
  772. pmId, _ := pmRes.LastInsertId()
  773. permIds = append(permIds, pmId)
  774. }
  775. t.Cleanup(func() {
  776. testutil.CleanTable(ctx, conn, "`sys_perm`", permIds...)
  777. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  778. })
  779. srv := NewPermServer(svcCtx)
  780. resp, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  781. AppKey: uid,
  782. AppSecret: "secret1",
  783. Perms: []*pb.PermItem{
  784. {Code: fmt.Sprintf("%s_c0", uid), Name: "p"},
  785. {Code: fmt.Sprintf("%s_c1", uid), Name: "p"},
  786. },
  787. })
  788. require.NoError(t, err)
  789. assert.Equal(t, int64(3), resp.Disabled)
  790. }
  791. // TC-0222: MEMBER-DENY覆盖
  792. func TestGetUserPerms_MemberDENYOverride(t *testing.T) {
  793. ctx := context.Background()
  794. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  795. conn := testutil.GetTestSqlConn()
  796. now := time.Now().Unix()
  797. uid := testutil.UniqueId()
  798. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  799. Username: uid, Password: testutil.HashPassword("pass"), Nickname: "nick",
  800. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  801. Status: 1, CreateTime: now, UpdateTime: now,
  802. })
  803. require.NoError(t, err)
  804. uId, _ := uRes.LastInsertId()
  805. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  806. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: bcryptHash(t, "secret1"),
  807. Status: 1, CreateTime: now, UpdateTime: now,
  808. })
  809. require.NoError(t, err)
  810. pId, _ := pRes.LastInsertId()
  811. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  812. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  813. Status: 1, CreateTime: now, UpdateTime: now,
  814. })
  815. require.NoError(t, err)
  816. mbrId, _ := mbrRes.LastInsertId()
  817. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  818. ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
  819. CreateTime: now, UpdateTime: now,
  820. })
  821. require.NoError(t, err)
  822. roleId, _ := roleRes.LastInsertId()
  823. permARes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  824. ProductCode: uid, Name: "permA", Code: uid + "_pA",
  825. Status: 1, CreateTime: now, UpdateTime: now,
  826. })
  827. require.NoError(t, err)
  828. permAId, _ := permARes.LastInsertId()
  829. permBRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  830. ProductCode: uid, Name: "permB", Code: uid + "_pB",
  831. Status: 1, CreateTime: now, UpdateTime: now,
  832. })
  833. require.NoError(t, err)
  834. permBId, _ := permBRes.LastInsertId()
  835. urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
  836. UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
  837. })
  838. require.NoError(t, err)
  839. urId, _ := urRes.LastInsertId()
  840. rpARes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  841. RoleId: roleId, PermId: permAId, CreateTime: now, UpdateTime: now,
  842. })
  843. require.NoError(t, err)
  844. rpAId, _ := rpARes.LastInsertId()
  845. rpBRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  846. RoleId: roleId, PermId: permBId, CreateTime: now, UpdateTime: now,
  847. })
  848. require.NoError(t, err)
  849. rpBId, _ := rpBRes.LastInsertId()
  850. upRes, err := svcCtx.SysUserPermModel.Insert(ctx, &userPermModel.SysUserPerm{
  851. UserId: uId, PermId: permAId, Effect: "DENY",
  852. CreateTime: now, UpdateTime: now,
  853. })
  854. require.NoError(t, err)
  855. upId, _ := upRes.LastInsertId()
  856. t.Cleanup(func() {
  857. testutil.CleanTable(ctx, conn, "`sys_user_perm`", upId)
  858. testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpAId, rpBId)
  859. testutil.CleanTable(ctx, conn, "`sys_user_role`", urId)
  860. testutil.CleanTable(ctx, conn, "`sys_perm`", permAId, permBId)
  861. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  862. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  863. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  864. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  865. })
  866. srv := NewPermServer(svcCtx)
  867. resp, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
  868. UserId: uId,
  869. ProductCode: uid,
  870. AppKey: uid + "_k",
  871. AppSecret: "secret1",
  872. })
  873. require.NoError(t, err)
  874. assert.Equal(t, "MEMBER", resp.MemberType)
  875. assert.Contains(t, resp.Perms, uid+"_pB")
  876. assert.NotContains(t, resp.Perms, uid+"_pA")
  877. }
  878. // TC-0217: gRPC VerifyToken 用户已冻结返回valid=false(H-4修复验证)
  879. func TestVerifyToken_FrozenUserReturnsInvalid(t *testing.T) {
  880. ctx := context.Background()
  881. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  882. conn := testutil.GetTestSqlConn()
  883. now := time.Now().Unix()
  884. uid := testutil.UniqueId()
  885. cfg := testutil.GetTestConfig()
  886. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  887. Username: uid, Password: testutil.HashPassword("pass"), Nickname: "frozen",
  888. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  889. Status: 2, CreateTime: now, UpdateTime: now,
  890. })
  891. require.NoError(t, err)
  892. uId, _ := uRes.LastInsertId()
  893. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", uId) })
  894. accessToken, err := authHelper.GenerateAccessToken(
  895. cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
  896. uId, uid, "", "MEMBER", 0,
  897. )
  898. require.NoError(t, err)
  899. srv := NewPermServer(svcCtx)
  900. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
  901. require.NoError(t, err)
  902. assert.False(t, resp.Valid, "frozen user token should be invalid")
  903. }
  904. // TC-0218: gRPC VerifyToken 非产品成员返回valid=false(H-4修复验证)
  905. func TestVerifyToken_NonMemberReturnsInvalid(t *testing.T) {
  906. ctx := context.Background()
  907. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  908. conn := testutil.GetTestSqlConn()
  909. now := time.Now().Unix()
  910. uid := testutil.UniqueId()
  911. pc := testutil.UniqueId()
  912. cfg := testutil.GetTestConfig()
  913. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  914. Username: uid, Password: testutil.HashPassword("pass"), Nickname: "user",
  915. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  916. Status: 1, CreateTime: now, UpdateTime: now,
  917. })
  918. require.NoError(t, err)
  919. uId, _ := uRes.LastInsertId()
  920. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  921. Code: pc, Name: "prod", AppKey: testutil.UniqueId(), AppSecret: "s",
  922. Status: 1, CreateTime: now, UpdateTime: now,
  923. })
  924. require.NoError(t, err)
  925. pId, _ := pRes.LastInsertId()
  926. t.Cleanup(func() {
  927. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  928. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  929. })
  930. accessToken, err := authHelper.GenerateAccessToken(
  931. cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
  932. uId, uid, pc, "MEMBER", 0,
  933. )
  934. require.NoError(t, err)
  935. srv := NewPermServer(svcCtx)
  936. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
  937. require.NoError(t, err)
  938. assert.False(t, resp.Valid, "non-member user with productCode should be invalid")
  939. }
  940. // TC-0219: gRPC VerifyToken 返回实时权限和成员类型(H-4修复验证)
  941. func TestVerifyToken_ReturnsRealtimeData(t *testing.T) {
  942. ctx := context.Background()
  943. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  944. conn := testutil.GetTestSqlConn()
  945. now := time.Now().Unix()
  946. uid := testutil.UniqueId()
  947. cfg := testutil.GetTestConfig()
  948. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  949. Username: uid, Password: testutil.HashPassword("pass"), Nickname: "user",
  950. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  951. Status: 1, CreateTime: now, UpdateTime: now,
  952. })
  953. require.NoError(t, err)
  954. uId, _ := uRes.LastInsertId()
  955. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  956. Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: "s",
  957. Status: 1, CreateTime: now, UpdateTime: now,
  958. })
  959. require.NoError(t, err)
  960. pId, _ := pRes.LastInsertId()
  961. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  962. ProductCode: uid, UserId: uId, MemberType: "ADMIN",
  963. Status: 1, CreateTime: now, UpdateTime: now,
  964. })
  965. require.NoError(t, err)
  966. mbrId, _ := mbrRes.LastInsertId()
  967. permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  968. ProductCode: uid, Name: "realtime_perm", Code: uid + "_rt",
  969. Status: 1, CreateTime: now, UpdateTime: now,
  970. })
  971. require.NoError(t, err)
  972. permId, _ := permRes.LastInsertId()
  973. t.Cleanup(func() {
  974. testutil.CleanTable(ctx, conn, "`sys_perm`", permId)
  975. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  976. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  977. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  978. })
  979. accessToken, err := authHelper.GenerateAccessToken(
  980. cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
  981. uId, uid, uid, "MEMBER", 0,
  982. )
  983. require.NoError(t, err)
  984. svcCtx.UserDetailsLoader.Clean(ctx, uId)
  985. srv := NewPermServer(svcCtx)
  986. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
  987. require.NoError(t, err)
  988. assert.True(t, resp.Valid)
  989. assert.Equal(t, "ADMIN", resp.MemberType, "should return realtime memberType, not token's")
  990. assert.Contains(t, resp.Perms, uid+"_rt", "should return realtime perms")
  991. }
  992. // TC-0206: gRPC Login 产品成员被禁用时拒绝(H-3修复验证)
  993. func TestLogin_DisabledMemberRejected(t *testing.T) {
  994. ctx := context.Background()
  995. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  996. conn := testutil.GetTestSqlConn()
  997. now := time.Now().Unix()
  998. uid := testutil.UniqueId()
  999. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  1000. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  1001. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  1002. Status: 1, CreateTime: now, UpdateTime: now,
  1003. })
  1004. require.NoError(t, err)
  1005. uId, _ := uRes.LastInsertId()
  1006. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  1007. Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: "s1",
  1008. Status: 1, CreateTime: now, UpdateTime: now,
  1009. })
  1010. require.NoError(t, err)
  1011. pId, _ := pRes.LastInsertId()
  1012. pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  1013. ProductCode: uid, UserId: uId, MemberType: "MEMBER", Status: 2,
  1014. CreateTime: now, UpdateTime: now,
  1015. })
  1016. require.NoError(t, err)
  1017. pmId, _ := pmRes.LastInsertId()
  1018. t.Cleanup(func() {
  1019. testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId)
  1020. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  1021. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  1022. })
  1023. srv := NewPermServer(svcCtx)
  1024. _, err = srv.Login(ctx, &pb.LoginReq{
  1025. Username: uid,
  1026. Password: "pass123",
  1027. ProductCode: uid,
  1028. })
  1029. require.Error(t, err)
  1030. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  1031. assert.Equal(t, "您在该产品下的成员资格已被禁用", status.Convert(err).Message())
  1032. }
  1033. // helper: create a JWT with no userId claim
  1034. func createTokenWithoutUserId(secret string) string {
  1035. claims := jwt.MapClaims{
  1036. "username": "test",
  1037. "exp": time.Now().Add(time.Hour).Unix(),
  1038. }
  1039. token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  1040. s, _ := token.SignedString([]byte(secret))
  1041. return s
  1042. }