loginLogic_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. package pub
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "testing"
  7. "time"
  8. permModel "perms-system-server/internal/model/perm"
  9. productModel "perms-system-server/internal/model/product"
  10. productmemberModel "perms-system-server/internal/model/productmember"
  11. userModel "perms-system-server/internal/model/user"
  12. "perms-system-server/internal/response"
  13. "perms-system-server/internal/svc"
  14. "perms-system-server/internal/testutil"
  15. "perms-system-server/internal/types"
  16. "github.com/stretchr/testify/assert"
  17. "github.com/stretchr/testify/require"
  18. )
  19. func newTestSvcCtx() *svc.ServiceContext {
  20. return svc.NewServiceContext(testutil.GetTestConfig())
  21. }
  22. func insertTestUser(t *testing.T, ctx context.Context, svcCtx *svc.ServiceContext, username, password string, status, isSuperAdmin int64) (int64, func()) {
  23. t.Helper()
  24. conn := testutil.GetTestSqlConn()
  25. now := time.Now().Unix()
  26. hashed := testutil.HashPassword(password)
  27. res, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  28. Username: username,
  29. Password: hashed,
  30. Nickname: username,
  31. Avatar: sql.NullString{},
  32. Email: username + "@test.com",
  33. Phone: "13800000000",
  34. Remark: "",
  35. DeptId: 0,
  36. IsSuperAdmin: isSuperAdmin,
  37. MustChangePassword: 2,
  38. Status: status,
  39. CreateTime: now,
  40. UpdateTime: now,
  41. })
  42. require.NoError(t, err)
  43. id, _ := res.LastInsertId()
  44. cleanup := func() {
  45. testutil.CleanTable(ctx, conn, "`sys_user`", id)
  46. }
  47. return id, cleanup
  48. }
  49. func insertTestProduct(t *testing.T, ctx context.Context, svcCtx *svc.ServiceContext, code, appKey, appSecret string) (int64, func()) {
  50. t.Helper()
  51. conn := testutil.GetTestSqlConn()
  52. now := time.Now().Unix()
  53. res, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
  54. Code: code,
  55. Name: code,
  56. AppKey: appKey,
  57. AppSecret: appSecret,
  58. Status: 1,
  59. CreateTime: now,
  60. UpdateTime: now,
  61. })
  62. require.NoError(t, err)
  63. id, _ := res.LastInsertId()
  64. cleanup := func() {
  65. testutil.CleanTable(ctx, conn, "`sys_product`", id)
  66. }
  67. return id, cleanup
  68. }
  69. // TC-0001: 正常登录(普通用户+productCode)
  70. func TestLogin_NormalWithProductCodeBasic(t *testing.T) {
  71. ctx := context.Background()
  72. svcCtx := newTestSvcCtx()
  73. username := testutil.UniqueId()
  74. password := "TestPass123"
  75. pc := testutil.UniqueId()
  76. _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 2)
  77. t.Cleanup(cleanUser)
  78. _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
  79. t.Cleanup(cleanProduct)
  80. logic := NewLoginLogic(ctx, svcCtx)
  81. resp, err := logic.Login(&types.LoginReq{
  82. Username: username,
  83. Password: password,
  84. ProductCode: pc,
  85. })
  86. require.NoError(t, err)
  87. require.NotNil(t, resp)
  88. assert.NotEmpty(t, resp.AccessToken)
  89. assert.NotEmpty(t, resp.RefreshToken)
  90. assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
  91. assert.Equal(t, username, resp.UserInfo.Username)
  92. assert.Empty(t, resp.UserInfo.MemberType)
  93. assert.Nil(t, resp.UserInfo.Perms)
  94. }
  95. // TC-0002: 正常登录-带productCode
  96. func TestLogin_NormalWithProductCode(t *testing.T) {
  97. ctx := context.Background()
  98. svcCtx := newTestSvcCtx()
  99. conn := testutil.GetTestSqlConn()
  100. username := testutil.UniqueId()
  101. password := "TestPass123"
  102. pc := testutil.UniqueId()
  103. now := time.Now().Unix()
  104. userId, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 2)
  105. t.Cleanup(cleanUser)
  106. _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
  107. t.Cleanup(cleanProduct)
  108. pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &productmemberModel.SysProductMember{
  109. ProductCode: pc, UserId: userId, MemberType: "ADMIN", Status: 1, CreateTime: now, UpdateTime: now,
  110. })
  111. require.NoError(t, err)
  112. pmId, _ := pmRes.LastInsertId()
  113. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId) })
  114. permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  115. ProductCode: pc, Name: "perm1", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now,
  116. })
  117. require.NoError(t, err)
  118. permId, _ := permRes.LastInsertId()
  119. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_perm`", permId) })
  120. logic := NewLoginLogic(ctx, svcCtx)
  121. resp, err := logic.Login(&types.LoginReq{
  122. Username: username,
  123. Password: password,
  124. ProductCode: pc,
  125. })
  126. require.NoError(t, err)
  127. require.NotNil(t, resp)
  128. assert.Equal(t, "ADMIN", resp.UserInfo.MemberType)
  129. assert.NotEmpty(t, resp.UserInfo.Perms)
  130. }
  131. // TC-0003: 超管通过产品端登录被拒绝
  132. func TestLogin_SuperAdminRejected(t *testing.T) {
  133. ctx := context.Background()
  134. svcCtx := newTestSvcCtx()
  135. username := testutil.UniqueId()
  136. password := "TestPass123"
  137. pc := testutil.UniqueId()
  138. _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 1)
  139. t.Cleanup(cleanUser)
  140. _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
  141. t.Cleanup(cleanProduct)
  142. logic := NewLoginLogic(ctx, svcCtx)
  143. resp, err := logic.Login(&types.LoginReq{
  144. Username: username,
  145. Password: password,
  146. ProductCode: pc,
  147. })
  148. require.Nil(t, resp)
  149. require.Error(t, err)
  150. var codeErr *response.CodeError
  151. require.True(t, errors.As(err, &codeErr))
  152. assert.Equal(t, 403, codeErr.Code())
  153. assert.Equal(t, "超级管理员不允许通过产品端登录,请使用管理后台", codeErr.Error())
  154. }
  155. // TC-0004: 超管无productCode被拒绝
  156. func TestLogin_SuperAdminWithoutProductCodeRejected(t *testing.T) {
  157. ctx := context.Background()
  158. svcCtx := newTestSvcCtx()
  159. username := testutil.UniqueId()
  160. password := "TestPass123"
  161. _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 1)
  162. t.Cleanup(cleanUser)
  163. logic := NewLoginLogic(ctx, svcCtx)
  164. resp, err := logic.Login(&types.LoginReq{
  165. Username: username,
  166. Password: password,
  167. })
  168. require.Nil(t, resp)
  169. require.Error(t, err)
  170. var codeErr *response.CodeError
  171. require.True(t, errors.As(err, &codeErr))
  172. assert.Equal(t, 403, codeErr.Code())
  173. assert.Equal(t, "超级管理员不允许通过产品端登录,请使用管理后台", codeErr.Error())
  174. }
  175. // TC-0005: 用户不存在
  176. func TestLogin_UserNotFound(t *testing.T) {
  177. ctx := context.Background()
  178. svcCtx := newTestSvcCtx()
  179. logic := NewLoginLogic(ctx, svcCtx)
  180. resp, err := logic.Login(&types.LoginReq{
  181. Username: "nonexistent_" + testutil.UniqueId(),
  182. Password: "whatever",
  183. })
  184. require.Nil(t, resp)
  185. require.Error(t, err)
  186. var codeErr *response.CodeError
  187. require.True(t, errors.As(err, &codeErr))
  188. assert.Equal(t, 401, codeErr.Code())
  189. assert.Equal(t, "用户名或密码错误", codeErr.Error())
  190. }
  191. // TC-0007: 密码错误
  192. func TestLogin_WrongPassword(t *testing.T) {
  193. ctx := context.Background()
  194. svcCtx := newTestSvcCtx()
  195. username := testutil.UniqueId()
  196. _, cleanUser := insertTestUser(t, ctx, svcCtx, username, "CorrectPass", 1, 2)
  197. t.Cleanup(cleanUser)
  198. logic := NewLoginLogic(ctx, svcCtx)
  199. resp, err := logic.Login(&types.LoginReq{
  200. Username: username,
  201. Password: "WrongPass",
  202. })
  203. require.Nil(t, resp)
  204. require.Error(t, err)
  205. var codeErr *response.CodeError
  206. require.True(t, errors.As(err, &codeErr))
  207. assert.Equal(t, 401, codeErr.Code())
  208. assert.Equal(t, "用户名或密码错误", codeErr.Error())
  209. }
  210. // TC-0008: 账号冻结
  211. func TestLogin_AccountFrozen(t *testing.T) {
  212. ctx := context.Background()
  213. svcCtx := newTestSvcCtx()
  214. username := testutil.UniqueId()
  215. password := "TestPass123"
  216. _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 2, 2)
  217. t.Cleanup(cleanUser)
  218. logic := NewLoginLogic(ctx, svcCtx)
  219. resp, err := logic.Login(&types.LoginReq{
  220. Username: username,
  221. Password: password,
  222. })
  223. require.Nil(t, resp)
  224. require.Error(t, err)
  225. var codeErr *response.CodeError
  226. require.True(t, errors.As(err, &codeErr))
  227. assert.Equal(t, 403, codeErr.Code())
  228. assert.Equal(t, "账号已被冻结", codeErr.Error())
  229. }
  230. // TC-0009: 非产品成员
  231. func TestLogin_NonMemberWithProductCode(t *testing.T) {
  232. ctx := context.Background()
  233. svcCtx := newTestSvcCtx()
  234. username := testutil.UniqueId()
  235. password := "TestPass123"
  236. pc := testutil.UniqueId()
  237. _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 2)
  238. t.Cleanup(cleanUser)
  239. _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
  240. t.Cleanup(cleanProduct)
  241. logic := NewLoginLogic(ctx, svcCtx)
  242. resp, err := logic.Login(&types.LoginReq{
  243. Username: username,
  244. Password: password,
  245. ProductCode: pc,
  246. })
  247. require.NoError(t, err)
  248. require.NotNil(t, resp)
  249. assert.Empty(t, resp.UserInfo.MemberType)
  250. assert.Nil(t, resp.UserInfo.Perms)
  251. }
  252. // TC-0010: DEVELOPER成员
  253. func TestLogin_DeveloperMember(t *testing.T) {
  254. ctx := context.Background()
  255. svcCtx := newTestSvcCtx()
  256. conn := testutil.GetTestSqlConn()
  257. username := testutil.UniqueId()
  258. password := "TestPass123"
  259. pc := testutil.UniqueId()
  260. now := time.Now().Unix()
  261. userId, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 2)
  262. t.Cleanup(cleanUser)
  263. _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
  264. t.Cleanup(cleanProduct)
  265. pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &productmemberModel.SysProductMember{
  266. ProductCode: pc, UserId: userId, MemberType: "DEVELOPER", Status: 1, CreateTime: now, UpdateTime: now,
  267. })
  268. require.NoError(t, err)
  269. pmId, _ := pmRes.LastInsertId()
  270. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId) })
  271. permCode := testutil.UniqueId()
  272. permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  273. ProductCode: pc, Name: "dev_perm", Code: permCode, Status: 1, CreateTime: now, UpdateTime: now,
  274. })
  275. require.NoError(t, err)
  276. permId, _ := permRes.LastInsertId()
  277. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_perm`", permId) })
  278. logic := NewLoginLogic(ctx, svcCtx)
  279. resp, err := logic.Login(&types.LoginReq{
  280. Username: username,
  281. Password: password,
  282. ProductCode: pc,
  283. })
  284. require.NoError(t, err)
  285. require.NotNil(t, resp)
  286. assert.Equal(t, "DEVELOPER", resp.UserInfo.MemberType)
  287. assert.Contains(t, resp.UserInfo.Perms, permCode)
  288. }
  289. // TC-0011: SQL注入
  290. func TestLogin_SQLInjection(t *testing.T) {
  291. ctx := context.Background()
  292. svcCtx := newTestSvcCtx()
  293. logic := NewLoginLogic(ctx, svcCtx)
  294. resp, err := logic.Login(&types.LoginReq{
  295. Username: "' OR 1=1 --",
  296. Password: "anything",
  297. })
  298. require.Nil(t, resp)
  299. require.Error(t, err)
  300. var codeErr *response.CodeError
  301. require.True(t, errors.As(err, &codeErr))
  302. assert.Equal(t, 401, codeErr.Code())
  303. assert.Equal(t, "用户名或密码错误", codeErr.Error())
  304. }