permserver_test.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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. "google.golang.org/grpc/codes"
  24. "google.golang.org/grpc/status"
  25. )
  26. // ---------- SyncPermissions ----------
  27. // TC-0161: 正常同步
  28. func TestSyncPermissions_Normal(t *testing.T) {
  29. ctx := context.Background()
  30. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  31. conn := testutil.GetTestSqlConn()
  32. now := time.Now().Unix()
  33. uid := testutil.UniqueId()
  34. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  35. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "secret1",
  36. Status: 1, CreateTime: now, UpdateTime: now,
  37. })
  38. require.NoError(t, err)
  39. pId, _ := pRes.LastInsertId()
  40. t.Cleanup(func() {
  41. testutil.CleanTableByField(ctx, conn, "`sys_perm`", "productCode", uid)
  42. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  43. })
  44. srv := NewPermServer(svcCtx)
  45. resp, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  46. AppKey: uid,
  47. AppSecret: "secret1",
  48. Perms: []*pb.PermItem{
  49. {Code: "perm_a", Name: "Perm A", Remark: "remark_a"},
  50. {Code: "perm_b", Name: "Perm B", Remark: "remark_b"},
  51. },
  52. })
  53. require.NoError(t, err)
  54. assert.Equal(t, int64(2), resp.Added)
  55. assert.Equal(t, int64(0), resp.Updated)
  56. assert.Equal(t, int64(0), resp.Disabled)
  57. resp2, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  58. AppKey: uid,
  59. AppSecret: "secret1",
  60. Perms: []*pb.PermItem{
  61. {Code: "perm_a", Name: "Perm A Updated", Remark: "remark_a"},
  62. },
  63. })
  64. require.NoError(t, err)
  65. assert.Equal(t, int64(0), resp2.Added)
  66. assert.Equal(t, int64(1), resp2.Updated)
  67. assert.Equal(t, int64(1), resp2.Disabled)
  68. }
  69. // TC-0162: appKey无效
  70. func TestSyncPermissions_InvalidAppKey(t *testing.T) {
  71. ctx := context.Background()
  72. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  73. srv := NewPermServer(svcCtx)
  74. _, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  75. AppKey: "nonexistent_key",
  76. AppSecret: "any",
  77. Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
  78. })
  79. require.Error(t, err)
  80. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  81. assert.Equal(t, "无效的appKey", status.Convert(err).Message())
  82. }
  83. // TC-0163: appSecret错误
  84. func TestSyncPermissions_WrongAppSecret(t *testing.T) {
  85. ctx := context.Background()
  86. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  87. conn := testutil.GetTestSqlConn()
  88. now := time.Now().Unix()
  89. uid := testutil.UniqueId()
  90. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  91. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "real_secret",
  92. Status: 1, CreateTime: now, UpdateTime: now,
  93. })
  94. require.NoError(t, err)
  95. pId, _ := pRes.LastInsertId()
  96. t.Cleanup(func() {
  97. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  98. })
  99. srv := NewPermServer(svcCtx)
  100. _, err = srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  101. AppKey: uid,
  102. AppSecret: "wrong_secret",
  103. Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
  104. })
  105. require.Error(t, err)
  106. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  107. assert.Equal(t, "appSecret验证失败", status.Convert(err).Message())
  108. }
  109. // TC-0164: 产品已禁用
  110. func TestSyncPermissions_ProductDisabled(t *testing.T) {
  111. ctx := context.Background()
  112. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  113. conn := testutil.GetTestSqlConn()
  114. now := time.Now().Unix()
  115. uid := testutil.UniqueId()
  116. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  117. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "secret1",
  118. Status: 2, CreateTime: now, UpdateTime: now,
  119. })
  120. require.NoError(t, err)
  121. pId, _ := pRes.LastInsertId()
  122. t.Cleanup(func() {
  123. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  124. })
  125. srv := NewPermServer(svcCtx)
  126. _, err = srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  127. AppKey: uid,
  128. AppSecret: "secret1",
  129. Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
  130. })
  131. require.Error(t, err)
  132. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  133. assert.Equal(t, "产品已被禁用", status.Convert(err).Message())
  134. }
  135. // ---------- Login ----------
  136. // TC-0166: 正常登录(普通用户+productCode)
  137. func TestLogin_Normal(t *testing.T) {
  138. ctx := context.Background()
  139. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  140. conn := testutil.GetTestSqlConn()
  141. now := time.Now().Unix()
  142. uid := testutil.UniqueId()
  143. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  144. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  145. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  146. Status: 1, CreateTime: now, UpdateTime: now,
  147. })
  148. require.NoError(t, err)
  149. uId, _ := uRes.LastInsertId()
  150. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  151. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  152. Status: 1, CreateTime: now, UpdateTime: now,
  153. })
  154. require.NoError(t, err)
  155. pId, _ := pRes.LastInsertId()
  156. t.Cleanup(func() {
  157. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  158. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  159. })
  160. srv := NewPermServer(svcCtx)
  161. resp, err := srv.Login(ctx, &pb.LoginReq{
  162. Username: uid,
  163. Password: "pass123",
  164. ProductCode: uid,
  165. })
  166. require.NoError(t, err)
  167. assert.NotEmpty(t, resp.AccessToken)
  168. assert.NotEmpty(t, resp.RefreshToken)
  169. assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
  170. assert.Equal(t, uId, resp.UserId)
  171. assert.Equal(t, uid, resp.Username)
  172. }
  173. // TC-0167: 用户不存在
  174. func TestLogin_UserNotFound(t *testing.T) {
  175. ctx := context.Background()
  176. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  177. srv := NewPermServer(svcCtx)
  178. _, err := srv.Login(ctx, &pb.LoginReq{
  179. Username: "nonexistent_user_xyz",
  180. Password: "any",
  181. ProductCode: "any_product",
  182. })
  183. require.Error(t, err)
  184. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  185. assert.Equal(t, "用户名或密码错误", status.Convert(err).Message())
  186. }
  187. // TC-0168: 密码错误
  188. func TestLogin_WrongPassword(t *testing.T) {
  189. ctx := context.Background()
  190. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  191. conn := testutil.GetTestSqlConn()
  192. now := time.Now().Unix()
  193. uid := testutil.UniqueId()
  194. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  195. Username: uid, Password: testutil.HashPassword("correct_pass"), Nickname: "nick",
  196. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  197. Status: 1, CreateTime: now, UpdateTime: now,
  198. })
  199. require.NoError(t, err)
  200. uId, _ := uRes.LastInsertId()
  201. t.Cleanup(func() {
  202. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  203. })
  204. srv := NewPermServer(svcCtx)
  205. _, err = srv.Login(ctx, &pb.LoginReq{
  206. Username: uid,
  207. Password: "wrong_pass",
  208. ProductCode: "any_product",
  209. })
  210. require.Error(t, err)
  211. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  212. assert.Equal(t, "用户名或密码错误", status.Convert(err).Message())
  213. }
  214. // TC-0169: 账号冻结
  215. func TestLogin_AccountFrozen(t *testing.T) {
  216. ctx := context.Background()
  217. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  218. conn := testutil.GetTestSqlConn()
  219. now := time.Now().Unix()
  220. uid := testutil.UniqueId()
  221. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  222. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  223. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  224. Status: 2, CreateTime: now, UpdateTime: now,
  225. })
  226. require.NoError(t, err)
  227. uId, _ := uRes.LastInsertId()
  228. t.Cleanup(func() {
  229. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  230. })
  231. srv := NewPermServer(svcCtx)
  232. _, err = srv.Login(ctx, &pb.LoginReq{
  233. Username: uid,
  234. Password: "pass123",
  235. ProductCode: "any_product",
  236. })
  237. require.Error(t, err)
  238. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  239. assert.Equal(t, "账号已被冻结", status.Convert(err).Message())
  240. }
  241. // TC-0170: 超管被拒绝
  242. func TestLogin_SuperAdminRejected(t *testing.T) {
  243. ctx := context.Background()
  244. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  245. conn := testutil.GetTestSqlConn()
  246. now := time.Now().Unix()
  247. uid := testutil.UniqueId()
  248. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  249. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "sa",
  250. Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
  251. Status: 1, CreateTime: now, UpdateTime: now,
  252. })
  253. require.NoError(t, err)
  254. uId, _ := uRes.LastInsertId()
  255. t.Cleanup(func() {
  256. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  257. })
  258. srv := NewPermServer(svcCtx)
  259. _, err = srv.Login(ctx, &pb.LoginReq{
  260. Username: uid,
  261. Password: "pass123",
  262. ProductCode: "any_product",
  263. })
  264. require.Error(t, err)
  265. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  266. assert.Equal(t, "超级管理员不允许通过产品端登录", status.Convert(err).Message())
  267. }
  268. // TC-0171: 普通用户+productCode
  269. func TestLogin_NormalUserWithProductCode(t *testing.T) {
  270. ctx := context.Background()
  271. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  272. conn := testutil.GetTestSqlConn()
  273. now := time.Now().Unix()
  274. uid := testutil.UniqueId()
  275. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  276. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  277. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  278. Status: 1, CreateTime: now, UpdateTime: now,
  279. })
  280. require.NoError(t, err)
  281. uId, _ := uRes.LastInsertId()
  282. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  283. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  284. Status: 1, CreateTime: now, UpdateTime: now,
  285. })
  286. require.NoError(t, err)
  287. pId, _ := pRes.LastInsertId()
  288. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  289. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  290. Status: 1, CreateTime: now, UpdateTime: now,
  291. })
  292. require.NoError(t, err)
  293. mbrId, _ := mbrRes.LastInsertId()
  294. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  295. ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
  296. CreateTime: now, UpdateTime: now,
  297. })
  298. require.NoError(t, err)
  299. roleId, _ := roleRes.LastInsertId()
  300. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  301. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  302. Status: 1, CreateTime: now, UpdateTime: now,
  303. })
  304. require.NoError(t, err)
  305. pm1Id, _ := pm1Res.LastInsertId()
  306. urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
  307. UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
  308. })
  309. require.NoError(t, err)
  310. urId, _ := urRes.LastInsertId()
  311. rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  312. RoleId: roleId, PermId: pm1Id, CreateTime: now, UpdateTime: now,
  313. })
  314. require.NoError(t, err)
  315. rpId, _ := rpRes.LastInsertId()
  316. t.Cleanup(func() {
  317. testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId)
  318. testutil.CleanTable(ctx, conn, "`sys_user_role`", urId)
  319. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  320. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  321. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  322. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  323. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  324. })
  325. srv := NewPermServer(svcCtx)
  326. resp, err := srv.Login(ctx, &pb.LoginReq{
  327. Username: uid,
  328. Password: "pass123",
  329. ProductCode: uid,
  330. })
  331. require.NoError(t, err)
  332. assert.Equal(t, "MEMBER", resp.MemberType)
  333. assert.Contains(t, resp.Perms, uid+"_c1")
  334. assert.NotEmpty(t, resp.AccessToken)
  335. assert.NotEmpty(t, resp.RefreshToken)
  336. }
  337. // TC-0510: productCode为空
  338. func TestLogin_EmptyProductCode(t *testing.T) {
  339. ctx := context.Background()
  340. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  341. srv := NewPermServer(svcCtx)
  342. _, err := srv.Login(ctx, &pb.LoginReq{
  343. Username: "anyuser",
  344. Password: "anypass",
  345. ProductCode: "",
  346. })
  347. require.Error(t, err)
  348. assert.Equal(t, codes.InvalidArgument, status.Code(err))
  349. assert.Equal(t, "productCode不能为空", status.Convert(err).Message())
  350. }
  351. // ---------- RefreshToken ----------
  352. // TC-0172: 正常刷新(refreshToken原样返回,不重新生成)
  353. func TestRefreshToken_Normal(t *testing.T) {
  354. ctx := context.Background()
  355. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  356. conn := testutil.GetTestSqlConn()
  357. now := time.Now().Unix()
  358. uid := testutil.UniqueId()
  359. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  360. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  361. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  362. Status: 1, CreateTime: now, UpdateTime: now,
  363. })
  364. require.NoError(t, err)
  365. uId, _ := uRes.LastInsertId()
  366. t.Cleanup(func() {
  367. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  368. })
  369. cfg := testutil.GetTestConfig()
  370. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, "")
  371. require.NoError(t, err)
  372. srv := NewPermServer(svcCtx)
  373. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  374. RefreshToken: refreshToken,
  375. })
  376. require.NoError(t, err)
  377. assert.NotEmpty(t, resp.AccessToken)
  378. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回,不重新生成")
  379. assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
  380. }
  381. // TC-0173: token无效
  382. func TestRefreshToken_InvalidToken(t *testing.T) {
  383. ctx := context.Background()
  384. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  385. srv := NewPermServer(svcCtx)
  386. _, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  387. RefreshToken: "invalid.token.string",
  388. })
  389. require.Error(t, err)
  390. assert.Equal(t, codes.Unauthenticated, status.Code(err))
  391. assert.Equal(t, "refreshToken无效或已过期", status.Convert(err).Message())
  392. }
  393. // TC-0174: 账号冻结
  394. func TestRefreshToken_AccountFrozen(t *testing.T) {
  395. ctx := context.Background()
  396. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  397. conn := testutil.GetTestSqlConn()
  398. now := time.Now().Unix()
  399. uid := testutil.UniqueId()
  400. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  401. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  402. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  403. Status: 2, CreateTime: now, UpdateTime: now,
  404. })
  405. require.NoError(t, err)
  406. uId, _ := uRes.LastInsertId()
  407. t.Cleanup(func() {
  408. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  409. })
  410. cfg := testutil.GetTestConfig()
  411. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, "")
  412. require.NoError(t, err)
  413. srv := NewPermServer(svcCtx)
  414. _, err = srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  415. RefreshToken: refreshToken,
  416. })
  417. require.Error(t, err)
  418. assert.Equal(t, codes.PermissionDenied, status.Code(err))
  419. assert.Equal(t, "账号已被冻结", status.Convert(err).Message())
  420. }
  421. // TC-0175: productCode回退到claims
  422. func TestRefreshToken_FallbackToClaimsProductCode(t *testing.T) {
  423. ctx := context.Background()
  424. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  425. conn := testutil.GetTestSqlConn()
  426. now := time.Now().Unix()
  427. uid := testutil.UniqueId()
  428. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  429. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  430. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  431. Status: 1, CreateTime: now, UpdateTime: now,
  432. })
  433. require.NoError(t, err)
  434. uId, _ := uRes.LastInsertId()
  435. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  436. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  437. Status: 1, CreateTime: now, UpdateTime: now,
  438. })
  439. require.NoError(t, err)
  440. pId, _ := pRes.LastInsertId()
  441. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  442. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  443. Status: 1, CreateTime: now, UpdateTime: now,
  444. })
  445. require.NoError(t, err)
  446. mbrId, _ := mbrRes.LastInsertId()
  447. t.Cleanup(func() {
  448. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  449. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  450. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  451. })
  452. cfg := testutil.GetTestConfig()
  453. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid)
  454. require.NoError(t, err)
  455. srv := NewPermServer(svcCtx)
  456. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  457. RefreshToken: refreshToken,
  458. ProductCode: "",
  459. })
  460. require.NoError(t, err)
  461. assert.NotEmpty(t, resp.AccessToken)
  462. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回")
  463. }
  464. // TC-0176: 超管+productCode
  465. func TestRefreshToken_SuperAdminWithProductCode(t *testing.T) {
  466. ctx := context.Background()
  467. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  468. conn := testutil.GetTestSqlConn()
  469. now := time.Now().Unix()
  470. uid := testutil.UniqueId()
  471. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  472. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "sa",
  473. Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
  474. Status: 1, CreateTime: now, UpdateTime: now,
  475. })
  476. require.NoError(t, err)
  477. uId, _ := uRes.LastInsertId()
  478. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  479. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  480. Status: 1, CreateTime: now, UpdateTime: now,
  481. })
  482. require.NoError(t, err)
  483. pId, _ := pRes.LastInsertId()
  484. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  485. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  486. Status: 1, CreateTime: now, UpdateTime: now,
  487. })
  488. require.NoError(t, err)
  489. pm1Id, _ := pm1Res.LastInsertId()
  490. t.Cleanup(func() {
  491. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  492. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  493. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  494. })
  495. cfg := testutil.GetTestConfig()
  496. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid)
  497. require.NoError(t, err)
  498. srv := NewPermServer(svcCtx)
  499. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  500. RefreshToken: refreshToken,
  501. ProductCode: uid,
  502. })
  503. require.NoError(t, err)
  504. assert.NotEmpty(t, resp.AccessToken)
  505. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回")
  506. }
  507. // TC-0177: 普通用户+productCode
  508. func TestRefreshToken_NormalUserWithProductCode(t *testing.T) {
  509. ctx := context.Background()
  510. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  511. conn := testutil.GetTestSqlConn()
  512. now := time.Now().Unix()
  513. uid := testutil.UniqueId()
  514. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  515. Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
  516. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  517. Status: 1, CreateTime: now, UpdateTime: now,
  518. })
  519. require.NoError(t, err)
  520. uId, _ := uRes.LastInsertId()
  521. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  522. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  523. Status: 1, CreateTime: now, UpdateTime: now,
  524. })
  525. require.NoError(t, err)
  526. pId, _ := pRes.LastInsertId()
  527. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  528. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  529. Status: 1, CreateTime: now, UpdateTime: now,
  530. })
  531. require.NoError(t, err)
  532. mbrId, _ := mbrRes.LastInsertId()
  533. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  534. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  535. Status: 1, CreateTime: now, UpdateTime: now,
  536. })
  537. require.NoError(t, err)
  538. pm1Id, _ := pm1Res.LastInsertId()
  539. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  540. ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
  541. CreateTime: now, UpdateTime: now,
  542. })
  543. require.NoError(t, err)
  544. roleId, _ := roleRes.LastInsertId()
  545. urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
  546. UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
  547. })
  548. require.NoError(t, err)
  549. urId, _ := urRes.LastInsertId()
  550. rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  551. RoleId: roleId, PermId: pm1Id, CreateTime: now, UpdateTime: now,
  552. })
  553. require.NoError(t, err)
  554. rpId, _ := rpRes.LastInsertId()
  555. t.Cleanup(func() {
  556. testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId)
  557. testutil.CleanTable(ctx, conn, "`sys_user_role`", urId)
  558. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  559. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  560. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  561. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  562. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  563. })
  564. cfg := testutil.GetTestConfig()
  565. refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid)
  566. require.NoError(t, err)
  567. srv := NewPermServer(svcCtx)
  568. resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
  569. RefreshToken: refreshToken,
  570. ProductCode: uid,
  571. })
  572. require.NoError(t, err)
  573. assert.NotEmpty(t, resp.AccessToken)
  574. assert.Equal(t, refreshToken, resp.RefreshToken, "refreshToken应原样返回")
  575. }
  576. // ---------- VerifyToken ----------
  577. // TC-0178: 有效token
  578. func TestVerifyToken_Valid(t *testing.T) {
  579. ctx := context.Background()
  580. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  581. cfg := testutil.GetTestConfig()
  582. accessToken, err := authHelper.GenerateAccessToken(
  583. cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
  584. 100, "testuser", "prod1", "ADMIN", []string{"perm_a", "perm_b"},
  585. )
  586. require.NoError(t, err)
  587. srv := NewPermServer(svcCtx)
  588. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
  589. require.NoError(t, err)
  590. assert.True(t, resp.Valid)
  591. assert.Equal(t, int64(100), resp.UserId)
  592. assert.Equal(t, "testuser", resp.Username)
  593. assert.Equal(t, "ADMIN", resp.MemberType)
  594. assert.ElementsMatch(t, []string{"perm_a", "perm_b"}, resp.Perms)
  595. }
  596. // TC-0179: 无效token
  597. func TestVerifyToken_Invalid(t *testing.T) {
  598. ctx := context.Background()
  599. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  600. srv := NewPermServer(svcCtx)
  601. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: "invalid.token.here"})
  602. require.NoError(t, err)
  603. assert.False(t, resp.Valid)
  604. }
  605. // TC-0180: 缺少userId
  606. func TestVerifyToken_MissingUserId(t *testing.T) {
  607. ctx := context.Background()
  608. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  609. cfg := testutil.GetTestConfig()
  610. // Generate a token without userId by using raw JWT
  611. token := createTokenWithoutUserId(cfg.Auth.AccessSecret)
  612. srv := NewPermServer(svcCtx)
  613. resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: token})
  614. require.NoError(t, err)
  615. assert.False(t, resp.Valid)
  616. }
  617. // ---------- GetUserPerms ----------
  618. // TC-0181: 用户不存在
  619. func TestGetUserPerms_UserNotFound(t *testing.T) {
  620. ctx := context.Background()
  621. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  622. srv := NewPermServer(svcCtx)
  623. _, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
  624. UserId: 999999999,
  625. ProductCode: "any_product",
  626. })
  627. require.Error(t, err)
  628. assert.Equal(t, codes.NotFound, status.Code(err))
  629. assert.Equal(t, "用户不存在", status.Convert(err).Message())
  630. }
  631. // TC-0182: 超管
  632. func TestGetUserPerms_SuperAdmin(t *testing.T) {
  633. ctx := context.Background()
  634. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  635. conn := testutil.GetTestSqlConn()
  636. now := time.Now().Unix()
  637. uid := testutil.UniqueId()
  638. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  639. Username: uid, Password: testutil.HashPassword("pass"), Nickname: "sa",
  640. Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
  641. Status: 1, CreateTime: now, UpdateTime: now,
  642. })
  643. require.NoError(t, err)
  644. uId, _ := uRes.LastInsertId()
  645. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  646. Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "s1",
  647. Status: 1, CreateTime: now, UpdateTime: now,
  648. })
  649. require.NoError(t, err)
  650. pId, _ := pRes.LastInsertId()
  651. pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  652. ProductCode: uid, Name: "p1", Code: uid + "_c1",
  653. Status: 1, CreateTime: now, UpdateTime: now,
  654. })
  655. require.NoError(t, err)
  656. pm1Id, _ := pm1Res.LastInsertId()
  657. mRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  658. ProductCode: uid, UserId: uId, MemberType: "ADMIN",
  659. Status: 1, CreateTime: now, UpdateTime: now,
  660. })
  661. require.NoError(t, err)
  662. mId, _ := mRes.LastInsertId()
  663. t.Cleanup(func() {
  664. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  665. testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
  666. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  667. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  668. })
  669. srv := NewPermServer(svcCtx)
  670. resp, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
  671. UserId: uId,
  672. ProductCode: uid,
  673. })
  674. require.NoError(t, err)
  675. assert.Equal(t, "SUPER_ADMIN", resp.MemberType)
  676. assert.Contains(t, resp.Perms, uid+"_c1")
  677. }
  678. // TC-0165: 验证disabled计数
  679. func TestSyncPermissions_VerifyDisabledCount(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: "secret1",
  687. Status: 1, CreateTime: now, UpdateTime: now,
  688. })
  689. require.NoError(t, err)
  690. pId, _ := pRes.LastInsertId()
  691. var permIds []int64
  692. for i := 0; i < 5; i++ {
  693. pmRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  694. ProductCode: uid, Name: "p", Code: fmt.Sprintf("%s_c%d", uid, i),
  695. Status: 1, CreateTime: now, UpdateTime: now,
  696. })
  697. require.NoError(t, err)
  698. pmId, _ := pmRes.LastInsertId()
  699. permIds = append(permIds, pmId)
  700. }
  701. t.Cleanup(func() {
  702. testutil.CleanTable(ctx, conn, "`sys_perm`", permIds...)
  703. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  704. })
  705. srv := NewPermServer(svcCtx)
  706. resp, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
  707. AppKey: uid,
  708. AppSecret: "secret1",
  709. Perms: []*pb.PermItem{
  710. {Code: fmt.Sprintf("%s_c0", uid), Name: "p"},
  711. {Code: fmt.Sprintf("%s_c1", uid), Name: "p"},
  712. },
  713. })
  714. require.NoError(t, err)
  715. assert.Equal(t, int64(3), resp.Disabled)
  716. }
  717. // TC-0183: MEMBER-DENY覆盖
  718. func TestGetUserPerms_MemberDENYOverride(t *testing.T) {
  719. ctx := context.Background()
  720. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  721. conn := testutil.GetTestSqlConn()
  722. now := time.Now().Unix()
  723. uid := testutil.UniqueId()
  724. uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  725. Username: uid, Password: testutil.HashPassword("pass"), Nickname: "nick",
  726. Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
  727. Status: 1, CreateTime: now, UpdateTime: now,
  728. })
  729. require.NoError(t, err)
  730. uId, _ := uRes.LastInsertId()
  731. pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  732. Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
  733. Status: 1, CreateTime: now, UpdateTime: now,
  734. })
  735. require.NoError(t, err)
  736. pId, _ := pRes.LastInsertId()
  737. mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
  738. ProductCode: uid, UserId: uId, MemberType: "MEMBER",
  739. Status: 1, CreateTime: now, UpdateTime: now,
  740. })
  741. require.NoError(t, err)
  742. mbrId, _ := mbrRes.LastInsertId()
  743. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  744. ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
  745. CreateTime: now, UpdateTime: now,
  746. })
  747. require.NoError(t, err)
  748. roleId, _ := roleRes.LastInsertId()
  749. permARes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  750. ProductCode: uid, Name: "permA", Code: uid + "_pA",
  751. Status: 1, CreateTime: now, UpdateTime: now,
  752. })
  753. require.NoError(t, err)
  754. permAId, _ := permARes.LastInsertId()
  755. permBRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  756. ProductCode: uid, Name: "permB", Code: uid + "_pB",
  757. Status: 1, CreateTime: now, UpdateTime: now,
  758. })
  759. require.NoError(t, err)
  760. permBId, _ := permBRes.LastInsertId()
  761. urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
  762. UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
  763. })
  764. require.NoError(t, err)
  765. urId, _ := urRes.LastInsertId()
  766. rpARes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  767. RoleId: roleId, PermId: permAId, CreateTime: now, UpdateTime: now,
  768. })
  769. require.NoError(t, err)
  770. rpAId, _ := rpARes.LastInsertId()
  771. rpBRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
  772. RoleId: roleId, PermId: permBId, CreateTime: now, UpdateTime: now,
  773. })
  774. require.NoError(t, err)
  775. rpBId, _ := rpBRes.LastInsertId()
  776. upRes, err := svcCtx.SysUserPermModel.Insert(ctx, &userPermModel.SysUserPerm{
  777. UserId: uId, PermId: permAId, Effect: "DENY",
  778. CreateTime: now, UpdateTime: now,
  779. })
  780. require.NoError(t, err)
  781. upId, _ := upRes.LastInsertId()
  782. t.Cleanup(func() {
  783. testutil.CleanTable(ctx, conn, "`sys_user_perm`", upId)
  784. testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpAId, rpBId)
  785. testutil.CleanTable(ctx, conn, "`sys_user_role`", urId)
  786. testutil.CleanTable(ctx, conn, "`sys_perm`", permAId, permBId)
  787. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  788. testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
  789. testutil.CleanTable(ctx, conn, "`sys_product`", pId)
  790. testutil.CleanTable(ctx, conn, "`sys_user`", uId)
  791. })
  792. srv := NewPermServer(svcCtx)
  793. resp, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
  794. UserId: uId,
  795. ProductCode: uid,
  796. })
  797. require.NoError(t, err)
  798. assert.Equal(t, "MEMBER", resp.MemberType)
  799. assert.Contains(t, resp.Perms, uid+"_pB")
  800. assert.NotContains(t, resp.Perms, uid+"_pA")
  801. }
  802. // helper: create a JWT with no userId claim
  803. func createTokenWithoutUserId(secret string) string {
  804. claims := jwt.MapClaims{
  805. "username": "test",
  806. "exp": time.Now().Add(time.Hour).Unix(),
  807. }
  808. token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  809. s, _ := token.SignedString([]byte(secret))
  810. return s
  811. }