permserver_test.go 48 KB

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