users_test.go 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408
  1. // Copyright 2020 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package db
  5. import (
  6. "context"
  7. "fmt"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "testing"
  12. "time"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. "gorm.io/gorm"
  16. "gogs.io/gogs/internal/auth"
  17. "gogs.io/gogs/internal/conf"
  18. "gogs.io/gogs/internal/dbtest"
  19. "gogs.io/gogs/internal/dbutil"
  20. "gogs.io/gogs/internal/errutil"
  21. "gogs.io/gogs/internal/osutil"
  22. "gogs.io/gogs/internal/repoutil"
  23. "gogs.io/gogs/internal/userutil"
  24. "gogs.io/gogs/public"
  25. )
  26. func TestUser_BeforeCreate(t *testing.T) {
  27. now := time.Now()
  28. db := &gorm.DB{
  29. Config: &gorm.Config{
  30. SkipDefaultTransaction: true,
  31. NowFunc: func() time.Time {
  32. return now
  33. },
  34. },
  35. }
  36. t.Run("CreatedUnix has been set", func(t *testing.T) {
  37. user := &User{
  38. CreatedUnix: 1,
  39. }
  40. _ = user.BeforeCreate(db)
  41. assert.Equal(t, int64(1), user.CreatedUnix)
  42. assert.Equal(t, int64(0), user.UpdatedUnix)
  43. })
  44. t.Run("CreatedUnix has not been set", func(t *testing.T) {
  45. user := &User{}
  46. _ = user.BeforeCreate(db)
  47. assert.Equal(t, db.NowFunc().Unix(), user.CreatedUnix)
  48. assert.Equal(t, db.NowFunc().Unix(), user.UpdatedUnix)
  49. })
  50. }
  51. func TestUser_AfterFind(t *testing.T) {
  52. now := time.Now()
  53. db := &gorm.DB{
  54. Config: &gorm.Config{
  55. SkipDefaultTransaction: true,
  56. NowFunc: func() time.Time {
  57. return now
  58. },
  59. },
  60. }
  61. user := &User{
  62. FullName: "user1<script src=http://localhost:8181/xss.js>",
  63. CreatedUnix: now.Unix(),
  64. UpdatedUnix: now.Unix(),
  65. }
  66. _ = user.AfterFind(db)
  67. assert.Equal(t, "user1", user.FullName)
  68. assert.Equal(t, user.CreatedUnix, user.Created.Unix())
  69. assert.Equal(t, user.UpdatedUnix, user.Updated.Unix())
  70. }
  71. func TestUsers(t *testing.T) {
  72. if testing.Short() {
  73. t.Skip()
  74. }
  75. t.Parallel()
  76. ctx := context.Background()
  77. tables := []any{
  78. new(User), new(EmailAddress), new(Repository), new(Follow), new(PullRequest), new(PublicKey), new(OrgUser),
  79. new(Watch), new(Star), new(Issue), new(AccessToken), new(Collaboration), new(Action), new(IssueUser),
  80. new(Access),
  81. }
  82. db := &users{
  83. DB: dbtest.NewDB(t, "users", tables...),
  84. }
  85. for _, tc := range []struct {
  86. name string
  87. test func(t *testing.T, ctx context.Context, db *users)
  88. }{
  89. {"Authenticate", usersAuthenticate},
  90. {"ChangeUsername", usersChangeUsername},
  91. {"Count", usersCount},
  92. {"Create", usersCreate},
  93. {"DeleteCustomAvatar", usersDeleteCustomAvatar},
  94. {"DeleteByID", usersDeleteByID},
  95. {"DeleteInactivated", usersDeleteInactivated},
  96. {"GetByEmail", usersGetByEmail},
  97. {"GetByID", usersGetByID},
  98. {"GetByUsername", usersGetByUsername},
  99. {"GetByKeyID", usersGetByKeyID},
  100. {"GetMailableEmailsByUsernames", usersGetMailableEmailsByUsernames},
  101. {"IsUsernameUsed", usersIsUsernameUsed},
  102. {"List", usersList},
  103. {"ListFollowers", usersListFollowers},
  104. {"ListFollowings", usersListFollowings},
  105. {"SearchByName", usersSearchByName},
  106. {"Update", usersUpdate},
  107. {"UseCustomAvatar", usersUseCustomAvatar},
  108. {"AddEmail", usersAddEmail},
  109. {"GetEmail", usersGetEmail},
  110. {"ListEmails", usersListEmails},
  111. {"MarkEmailActivated", usersMarkEmailActivated},
  112. {"MarkEmailPrimary", usersMarkEmailPrimary},
  113. {"DeleteEmail", usersDeleteEmail},
  114. {"Follow", usersFollow},
  115. {"IsFollowing", usersIsFollowing},
  116. {"Unfollow", usersUnfollow},
  117. } {
  118. t.Run(tc.name, func(t *testing.T) {
  119. t.Cleanup(func() {
  120. err := clearTables(t, db.DB, tables...)
  121. require.NoError(t, err)
  122. })
  123. tc.test(t, ctx, db)
  124. })
  125. if t.Failed() {
  126. break
  127. }
  128. }
  129. }
  130. func usersAuthenticate(t *testing.T, ctx context.Context, db *users) {
  131. password := "pa$$word"
  132. alice, err := db.Create(ctx, "alice", "alice@example.com",
  133. CreateUserOptions{
  134. Password: password,
  135. },
  136. )
  137. require.NoError(t, err)
  138. t.Run("user not found", func(t *testing.T) {
  139. _, err := db.Authenticate(ctx, "bob", password, -1)
  140. wantErr := auth.ErrBadCredentials{Args: map[string]any{"login": "bob"}}
  141. assert.Equal(t, wantErr, err)
  142. })
  143. t.Run("invalid password", func(t *testing.T) {
  144. _, err := db.Authenticate(ctx, alice.Name, "bad_password", -1)
  145. wantErr := auth.ErrBadCredentials{Args: map[string]any{"login": alice.Name, "userID": alice.ID}}
  146. assert.Equal(t, wantErr, err)
  147. })
  148. t.Run("via email and password", func(t *testing.T) {
  149. user, err := db.Authenticate(ctx, alice.Email, password, -1)
  150. require.NoError(t, err)
  151. assert.Equal(t, alice.Name, user.Name)
  152. })
  153. t.Run("via username and password", func(t *testing.T) {
  154. user, err := db.Authenticate(ctx, alice.Name, password, -1)
  155. require.NoError(t, err)
  156. assert.Equal(t, alice.Name, user.Name)
  157. })
  158. t.Run("login source mismatch", func(t *testing.T) {
  159. _, err := db.Authenticate(ctx, alice.Email, password, 1)
  160. gotErr := fmt.Sprintf("%v", err)
  161. wantErr := ErrLoginSourceMismatch{args: map[string]any{"actual": 0, "expect": 1}}.Error()
  162. assert.Equal(t, wantErr, gotErr)
  163. })
  164. t.Run("via login source", func(t *testing.T) {
  165. mockLoginSources := NewMockLoginSourcesStore()
  166. mockLoginSources.GetByIDFunc.SetDefaultHook(func(ctx context.Context, id int64) (*LoginSource, error) {
  167. mockProvider := NewMockProvider()
  168. mockProvider.AuthenticateFunc.SetDefaultReturn(&auth.ExternalAccount{}, nil)
  169. s := &LoginSource{
  170. IsActived: true,
  171. Provider: mockProvider,
  172. }
  173. return s, nil
  174. })
  175. setMockLoginSourcesStore(t, mockLoginSources)
  176. bob, err := db.Create(ctx, "bob", "bob@example.com",
  177. CreateUserOptions{
  178. Password: password,
  179. LoginSource: 1,
  180. },
  181. )
  182. require.NoError(t, err)
  183. user, err := db.Authenticate(ctx, bob.Email, password, 1)
  184. require.NoError(t, err)
  185. assert.Equal(t, bob.Name, user.Name)
  186. })
  187. t.Run("new user via login source", func(t *testing.T) {
  188. mockLoginSources := NewMockLoginSourcesStore()
  189. mockLoginSources.GetByIDFunc.SetDefaultHook(func(ctx context.Context, id int64) (*LoginSource, error) {
  190. mockProvider := NewMockProvider()
  191. mockProvider.AuthenticateFunc.SetDefaultReturn(
  192. &auth.ExternalAccount{
  193. Name: "cindy",
  194. Email: "cindy@example.com",
  195. },
  196. nil,
  197. )
  198. s := &LoginSource{
  199. IsActived: true,
  200. Provider: mockProvider,
  201. }
  202. return s, nil
  203. })
  204. setMockLoginSourcesStore(t, mockLoginSources)
  205. user, err := db.Authenticate(ctx, "cindy", password, 1)
  206. require.NoError(t, err)
  207. assert.Equal(t, "cindy", user.Name)
  208. user, err = db.GetByUsername(ctx, "cindy")
  209. require.NoError(t, err)
  210. assert.Equal(t, "cindy@example.com", user.Email)
  211. })
  212. }
  213. func usersChangeUsername(t *testing.T, ctx context.Context, db *users) {
  214. alice, err := db.Create(
  215. ctx,
  216. "alice",
  217. "alice@example.com",
  218. CreateUserOptions{
  219. Activated: true,
  220. },
  221. )
  222. require.NoError(t, err)
  223. t.Run("name not allowed", func(t *testing.T) {
  224. err := db.ChangeUsername(ctx, alice.ID, "-")
  225. wantErr := ErrNameNotAllowed{
  226. args: errutil.Args{
  227. "reason": "reserved",
  228. "name": "-",
  229. },
  230. }
  231. assert.Equal(t, wantErr, err)
  232. })
  233. t.Run("name already exists", func(t *testing.T) {
  234. bob, err := db.Create(
  235. ctx,
  236. "bob",
  237. "bob@example.com",
  238. CreateUserOptions{
  239. Activated: true,
  240. },
  241. )
  242. require.NoError(t, err)
  243. err = db.ChangeUsername(ctx, alice.ID, bob.Name)
  244. wantErr := ErrUserAlreadyExist{
  245. args: errutil.Args{
  246. "name": bob.Name,
  247. },
  248. }
  249. assert.Equal(t, wantErr, err)
  250. })
  251. tempRepositoryRoot := filepath.Join(os.TempDir(), "usersChangeUsername-tempRepositoryRoot")
  252. conf.SetMockRepository(
  253. t,
  254. conf.RepositoryOpts{
  255. Root: tempRepositoryRoot,
  256. },
  257. )
  258. err = os.RemoveAll(tempRepositoryRoot)
  259. require.NoError(t, err)
  260. defer func() { _ = os.RemoveAll(tempRepositoryRoot) }()
  261. tempServerAppDataPath := filepath.Join(os.TempDir(), "usersChangeUsername-tempServerAppDataPath")
  262. conf.SetMockServer(
  263. t,
  264. conf.ServerOpts{
  265. AppDataPath: tempServerAppDataPath,
  266. },
  267. )
  268. err = os.RemoveAll(tempServerAppDataPath)
  269. require.NoError(t, err)
  270. defer func() { _ = os.RemoveAll(tempServerAppDataPath) }()
  271. repo, err := NewReposStore(db.DB).Create(
  272. ctx,
  273. alice.ID,
  274. CreateRepoOptions{
  275. Name: "test-repo-1",
  276. },
  277. )
  278. require.NoError(t, err)
  279. // TODO: Use PullRequests.Create to replace SQL hack when the method is available.
  280. err = db.Exec(`INSERT INTO pull_request (head_user_name) VALUES (?)`, alice.Name).Error
  281. require.NoError(t, err)
  282. err = db.Model(&User{}).Where("id = ?", alice.ID).Update("updated_unix", 0).Error
  283. require.NoError(t, err)
  284. err = os.MkdirAll(repoutil.UserPath(alice.Name), os.ModePerm)
  285. require.NoError(t, err)
  286. err = os.MkdirAll(repoutil.RepositoryLocalPath(repo.ID), os.ModePerm)
  287. require.NoError(t, err)
  288. err = os.MkdirAll(repoutil.RepositoryLocalWikiPath(repo.ID), os.ModePerm)
  289. require.NoError(t, err)
  290. // Make sure mock data is set up correctly
  291. // TODO: Use PullRequests.GetByID to replace SQL hack when the method is available.
  292. var headUserName string
  293. err = db.Model(&PullRequest{}).Select("head_user_name").Row().Scan(&headUserName)
  294. require.NoError(t, err)
  295. assert.Equal(t, headUserName, alice.Name)
  296. var updatedUnix int64
  297. err = db.Model(&User{}).Select("updated_unix").Where("id = ?", alice.ID).Row().Scan(&updatedUnix)
  298. require.NoError(t, err)
  299. assert.Equal(t, int64(0), updatedUnix)
  300. assert.True(t, osutil.IsExist(repoutil.UserPath(alice.Name)))
  301. assert.True(t, osutil.IsExist(repoutil.RepositoryLocalPath(repo.ID)))
  302. assert.True(t, osutil.IsExist(repoutil.RepositoryLocalWikiPath(repo.ID)))
  303. const newUsername = "alice-new"
  304. err = db.ChangeUsername(ctx, alice.ID, newUsername)
  305. require.NoError(t, err)
  306. // TODO: Use PullRequests.GetByID to replace SQL hack when the method is available.
  307. err = db.Model(&PullRequest{}).Select("head_user_name").Row().Scan(&headUserName)
  308. require.NoError(t, err)
  309. assert.Equal(t, headUserName, newUsername)
  310. assert.True(t, osutil.IsExist(repoutil.UserPath(newUsername)))
  311. assert.False(t, osutil.IsExist(repoutil.UserPath(alice.Name)))
  312. assert.False(t, osutil.IsExist(repoutil.RepositoryLocalPath(repo.ID)))
  313. assert.False(t, osutil.IsExist(repoutil.RepositoryLocalWikiPath(repo.ID)))
  314. alice, err = db.GetByID(ctx, alice.ID)
  315. require.NoError(t, err)
  316. assert.Equal(t, newUsername, alice.Name)
  317. assert.Equal(t, db.NowFunc().Unix(), alice.UpdatedUnix)
  318. // Change the cases of the username should just be fine
  319. err = db.ChangeUsername(ctx, alice.ID, strings.ToUpper(newUsername))
  320. require.NoError(t, err)
  321. alice, err = db.GetByID(ctx, alice.ID)
  322. require.NoError(t, err)
  323. assert.Equal(t, strings.ToUpper(newUsername), alice.Name)
  324. }
  325. func usersCount(t *testing.T, ctx context.Context, db *users) {
  326. // Has no user initially
  327. got := db.Count(ctx)
  328. assert.Equal(t, int64(0), got)
  329. _, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  330. require.NoError(t, err)
  331. got = db.Count(ctx)
  332. assert.Equal(t, int64(1), got)
  333. // Create an organization shouldn't count
  334. // TODO: Use Orgs.Create to replace SQL hack when the method is available.
  335. org1, err := db.Create(ctx, "org1", "org1@example.com", CreateUserOptions{})
  336. require.NoError(t, err)
  337. err = db.Exec(
  338. dbutil.Quote("UPDATE %s SET type = ? WHERE id = ?", "user"),
  339. UserTypeOrganization, org1.ID,
  340. ).Error
  341. require.NoError(t, err)
  342. got = db.Count(ctx)
  343. assert.Equal(t, int64(1), got)
  344. }
  345. func usersCreate(t *testing.T, ctx context.Context, db *users) {
  346. alice, err := db.Create(
  347. ctx,
  348. "alice",
  349. "alice@example.com",
  350. CreateUserOptions{
  351. Activated: true,
  352. },
  353. )
  354. require.NoError(t, err)
  355. t.Run("name not allowed", func(t *testing.T) {
  356. _, err := db.Create(ctx, "-", "", CreateUserOptions{})
  357. wantErr := ErrNameNotAllowed{
  358. args: errutil.Args{
  359. "reason": "reserved",
  360. "name": "-",
  361. },
  362. }
  363. assert.Equal(t, wantErr, err)
  364. })
  365. t.Run("name already exists", func(t *testing.T) {
  366. _, err := db.Create(ctx, alice.Name, "", CreateUserOptions{})
  367. wantErr := ErrUserAlreadyExist{
  368. args: errutil.Args{
  369. "name": alice.Name,
  370. },
  371. }
  372. assert.Equal(t, wantErr, err)
  373. })
  374. t.Run("email already exists", func(t *testing.T) {
  375. _, err := db.Create(ctx, "bob", alice.Email, CreateUserOptions{})
  376. wantErr := ErrEmailAlreadyUsed{
  377. args: errutil.Args{
  378. "email": alice.Email,
  379. },
  380. }
  381. assert.Equal(t, wantErr, err)
  382. })
  383. user, err := db.GetByUsername(ctx, alice.Name)
  384. require.NoError(t, err)
  385. assert.Equal(t, db.NowFunc().Format(time.RFC3339), user.Created.UTC().Format(time.RFC3339))
  386. assert.Equal(t, db.NowFunc().Format(time.RFC3339), user.Updated.UTC().Format(time.RFC3339))
  387. }
  388. func usersDeleteCustomAvatar(t *testing.T, ctx context.Context, db *users) {
  389. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  390. require.NoError(t, err)
  391. avatar, err := public.Files.ReadFile("img/avatar_default.png")
  392. require.NoError(t, err)
  393. avatarPath := userutil.CustomAvatarPath(alice.ID)
  394. _ = os.Remove(avatarPath)
  395. defer func() { _ = os.Remove(avatarPath) }()
  396. err = db.UseCustomAvatar(ctx, alice.ID, avatar)
  397. require.NoError(t, err)
  398. // Make sure avatar is saved and the user flag is updated.
  399. got := osutil.IsFile(avatarPath)
  400. assert.True(t, got)
  401. alice, err = db.GetByID(ctx, alice.ID)
  402. require.NoError(t, err)
  403. assert.True(t, alice.UseCustomAvatar)
  404. // Delete avatar should remove the file and revert the user flag.
  405. err = db.DeleteCustomAvatar(ctx, alice.ID)
  406. require.NoError(t, err)
  407. got = osutil.IsFile(avatarPath)
  408. assert.False(t, got)
  409. alice, err = db.GetByID(ctx, alice.ID)
  410. require.NoError(t, err)
  411. assert.False(t, alice.UseCustomAvatar)
  412. }
  413. func usersDeleteByID(t *testing.T, ctx context.Context, db *users) {
  414. reposStore := NewReposStore(db.DB)
  415. t.Run("user still has repository ownership", func(t *testing.T) {
  416. alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
  417. require.NoError(t, err)
  418. _, err = reposStore.Create(ctx, alice.ID, CreateRepoOptions{Name: "repo1"})
  419. require.NoError(t, err)
  420. err = db.DeleteByID(ctx, alice.ID, false)
  421. wantErr := ErrUserOwnRepos{errutil.Args{"userID": alice.ID}}
  422. assert.Equal(t, wantErr, err)
  423. })
  424. t.Run("user still has organization membership", func(t *testing.T) {
  425. bob, err := db.Create(ctx, "bob", "bob@exmaple.com", CreateUserOptions{})
  426. require.NoError(t, err)
  427. // TODO: Use Orgs.Create to replace SQL hack when the method is available.
  428. org1, err := db.Create(ctx, "org1", "org1@example.com", CreateUserOptions{})
  429. require.NoError(t, err)
  430. err = db.Exec(
  431. dbutil.Quote("UPDATE %s SET type = ? WHERE id IN (?)", "user"),
  432. UserTypeOrganization, org1.ID,
  433. ).Error
  434. require.NoError(t, err)
  435. // TODO: Use Orgs.Join to replace SQL hack when the method is available.
  436. err = db.Exec(`INSERT INTO org_user (uid, org_id) VALUES (?, ?)`, bob.ID, org1.ID).Error
  437. require.NoError(t, err)
  438. err = db.DeleteByID(ctx, bob.ID, false)
  439. wantErr := ErrUserHasOrgs{errutil.Args{"userID": bob.ID}}
  440. assert.Equal(t, wantErr, err)
  441. })
  442. cindy, err := db.Create(ctx, "cindy", "cindy@exmaple.com", CreateUserOptions{})
  443. require.NoError(t, err)
  444. frank, err := db.Create(ctx, "frank", "frank@exmaple.com", CreateUserOptions{})
  445. require.NoError(t, err)
  446. repo2, err := reposStore.Create(ctx, cindy.ID, CreateRepoOptions{Name: "repo2"})
  447. require.NoError(t, err)
  448. testUser, err := db.Create(ctx, "testUser", "testUser@exmaple.com", CreateUserOptions{})
  449. require.NoError(t, err)
  450. // Mock watches, stars and follows
  451. err = reposStore.Watch(ctx, testUser.ID, repo2.ID)
  452. require.NoError(t, err)
  453. err = reposStore.Star(ctx, testUser.ID, repo2.ID)
  454. require.NoError(t, err)
  455. err = db.Follow(ctx, testUser.ID, cindy.ID)
  456. require.NoError(t, err)
  457. err = db.Follow(ctx, frank.ID, testUser.ID)
  458. require.NoError(t, err)
  459. // Mock "authorized_keys" file
  460. // TODO: Use PublicKeys.Add to replace SQL hack when the method is available.
  461. publicKey := &PublicKey{
  462. OwnerID: testUser.ID,
  463. Name: "test-key",
  464. Fingerprint: "12:f8:7e:78:61:b4:bf:e2:de:24:15:96:4e:d4:72:53",
  465. Content: "test-key-content",
  466. }
  467. err = db.DB.Create(publicKey).Error
  468. require.NoError(t, err)
  469. tempSSHRootPath := filepath.Join(os.TempDir(), "usersDeleteByID-tempSSHRootPath")
  470. conf.SetMockSSH(t, conf.SSHOpts{RootPath: tempSSHRootPath})
  471. err = NewPublicKeysStore(db.DB).RewriteAuthorizedKeys()
  472. require.NoError(t, err)
  473. // Mock issue assignee
  474. // TODO: Use Issues.Assign to replace SQL hack when the method is available.
  475. issue := &Issue{
  476. RepoID: repo2.ID,
  477. Index: 1,
  478. PosterID: cindy.ID,
  479. Title: "test-issue",
  480. AssigneeID: testUser.ID,
  481. }
  482. err = db.DB.Create(issue).Error
  483. require.NoError(t, err)
  484. // Mock random entries in related tables
  485. for _, table := range []any{
  486. &AccessToken{UserID: testUser.ID},
  487. &Collaboration{UserID: testUser.ID},
  488. &Access{UserID: testUser.ID},
  489. &Action{UserID: testUser.ID},
  490. &IssueUser{UserID: testUser.ID},
  491. &EmailAddress{UserID: testUser.ID},
  492. } {
  493. err = db.DB.Create(table).Error
  494. require.NoError(t, err, "table for %T", table)
  495. }
  496. // Mock user directory
  497. tempRepositoryRoot := filepath.Join(os.TempDir(), "usersDeleteByID-tempRepositoryRoot")
  498. conf.SetMockRepository(t, conf.RepositoryOpts{Root: tempRepositoryRoot})
  499. tempUserPath := repoutil.UserPath(testUser.Name)
  500. err = os.MkdirAll(tempUserPath, os.ModePerm)
  501. require.NoError(t, err)
  502. // Mock user custom avatar
  503. tempPictureAvatarUploadPath := filepath.Join(os.TempDir(), "usersDeleteByID-tempPictureAvatarUploadPath")
  504. conf.SetMockPicture(t, conf.PictureOpts{AvatarUploadPath: tempPictureAvatarUploadPath})
  505. err = os.MkdirAll(tempPictureAvatarUploadPath, os.ModePerm)
  506. require.NoError(t, err)
  507. tempCustomAvatarPath := userutil.CustomAvatarPath(testUser.ID)
  508. err = os.WriteFile(tempCustomAvatarPath, []byte("test"), 0600)
  509. require.NoError(t, err)
  510. // Verify mock data
  511. repo2, err = reposStore.GetByID(ctx, repo2.ID)
  512. require.NoError(t, err)
  513. assert.Equal(t, 2, repo2.NumWatches) // The owner is watching the repo by default.
  514. assert.Equal(t, 1, repo2.NumStars)
  515. cindy, err = db.GetByID(ctx, cindy.ID)
  516. require.NoError(t, err)
  517. assert.Equal(t, 1, cindy.NumFollowers)
  518. frank, err = db.GetByID(ctx, frank.ID)
  519. require.NoError(t, err)
  520. assert.Equal(t, 1, frank.NumFollowing)
  521. authorizedKeys, err := os.ReadFile(authorizedKeysPath())
  522. require.NoError(t, err)
  523. assert.Contains(t, string(authorizedKeys), fmt.Sprintf("key-%d", publicKey.ID))
  524. assert.Contains(t, string(authorizedKeys), publicKey.Content)
  525. // TODO: Use Issues.GetByID to replace SQL hack when the method is available.
  526. err = db.DB.First(issue, issue.ID).Error
  527. require.NoError(t, err)
  528. assert.Equal(t, testUser.ID, issue.AssigneeID)
  529. relatedTables := []any{
  530. &Watch{UserID: testUser.ID},
  531. &Star{UserID: testUser.ID},
  532. &Follow{UserID: testUser.ID},
  533. &PublicKey{OwnerID: testUser.ID},
  534. &AccessToken{UserID: testUser.ID},
  535. &Collaboration{UserID: testUser.ID},
  536. &Access{UserID: testUser.ID},
  537. &Action{UserID: testUser.ID},
  538. &IssueUser{UserID: testUser.ID},
  539. &EmailAddress{UserID: testUser.ID},
  540. }
  541. for _, table := range relatedTables {
  542. var count int64
  543. err = db.DB.Model(table).Where(table).Count(&count).Error
  544. require.NoError(t, err, "table for %T", table)
  545. assert.NotZero(t, count, "table for %T", table)
  546. }
  547. assert.True(t, osutil.IsExist(tempUserPath))
  548. assert.True(t, osutil.IsExist(tempCustomAvatarPath))
  549. // Pull the trigger
  550. err = db.DeleteByID(ctx, testUser.ID, false)
  551. require.NoError(t, err)
  552. // Verify after-the-fact data
  553. repo2, err = reposStore.GetByID(ctx, repo2.ID)
  554. require.NoError(t, err)
  555. assert.Equal(t, 1, repo2.NumWatches) // The owner is watching the repo by default.
  556. assert.Equal(t, 0, repo2.NumStars)
  557. cindy, err = db.GetByID(ctx, cindy.ID)
  558. require.NoError(t, err)
  559. assert.Equal(t, 0, cindy.NumFollowers)
  560. frank, err = db.GetByID(ctx, frank.ID)
  561. require.NoError(t, err)
  562. assert.Equal(t, 0, frank.NumFollowing)
  563. authorizedKeys, err = os.ReadFile(authorizedKeysPath())
  564. require.NoError(t, err)
  565. assert.Empty(t, authorizedKeys)
  566. // TODO: Use Issues.GetByID to replace SQL hack when the method is available.
  567. err = db.DB.First(issue, issue.ID).Error
  568. require.NoError(t, err)
  569. assert.Equal(t, int64(0), issue.AssigneeID)
  570. for _, table := range []any{
  571. &Watch{UserID: testUser.ID},
  572. &Star{UserID: testUser.ID},
  573. &Follow{UserID: testUser.ID},
  574. &PublicKey{OwnerID: testUser.ID},
  575. &AccessToken{UserID: testUser.ID},
  576. &Collaboration{UserID: testUser.ID},
  577. &Access{UserID: testUser.ID},
  578. &Action{UserID: testUser.ID},
  579. &IssueUser{UserID: testUser.ID},
  580. &EmailAddress{UserID: testUser.ID},
  581. } {
  582. var count int64
  583. err = db.DB.Model(table).Where(table).Count(&count).Error
  584. require.NoError(t, err, "table for %T", table)
  585. assert.Equal(t, int64(0), count, "table for %T", table)
  586. }
  587. assert.False(t, osutil.IsExist(tempUserPath))
  588. assert.False(t, osutil.IsExist(tempCustomAvatarPath))
  589. _, err = db.GetByID(ctx, testUser.ID)
  590. wantErr := ErrUserNotExist{errutil.Args{"userID": testUser.ID}}
  591. assert.Equal(t, wantErr, err)
  592. }
  593. func usersDeleteInactivated(t *testing.T, ctx context.Context, db *users) {
  594. // User with repository ownership should be skipped
  595. alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
  596. require.NoError(t, err)
  597. reposStore := NewReposStore(db.DB)
  598. _, err = reposStore.Create(ctx, alice.ID, CreateRepoOptions{Name: "repo1"})
  599. require.NoError(t, err)
  600. // User with organization membership should be skipped
  601. bob, err := db.Create(ctx, "bob", "bob@exmaple.com", CreateUserOptions{})
  602. require.NoError(t, err)
  603. // TODO: Use Orgs.Create to replace SQL hack when the method is available.
  604. org1, err := db.Create(ctx, "org1", "org1@example.com", CreateUserOptions{})
  605. require.NoError(t, err)
  606. err = db.Exec(
  607. dbutil.Quote("UPDATE %s SET type = ? WHERE id IN (?)", "user"),
  608. UserTypeOrganization, org1.ID,
  609. ).Error
  610. require.NoError(t, err)
  611. // TODO: Use Orgs.Join to replace SQL hack when the method is available.
  612. err = db.Exec(`INSERT INTO org_user (uid, org_id) VALUES (?, ?)`, bob.ID, org1.ID).Error
  613. require.NoError(t, err)
  614. // User activated state should be skipped
  615. _, err = db.Create(ctx, "cindy", "cindy@exmaple.com", CreateUserOptions{Activated: true})
  616. require.NoError(t, err)
  617. // User meant to be deleted
  618. david, err := db.Create(ctx, "david", "david@exmaple.com", CreateUserOptions{})
  619. require.NoError(t, err)
  620. tempSSHRootPath := filepath.Join(os.TempDir(), "usersDeleteInactivated-tempSSHRootPath")
  621. conf.SetMockSSH(t, conf.SSHOpts{RootPath: tempSSHRootPath})
  622. err = db.DeleteInactivated()
  623. require.NoError(t, err)
  624. _, err = db.GetByID(ctx, david.ID)
  625. wantErr := ErrUserNotExist{errutil.Args{"userID": david.ID}}
  626. assert.Equal(t, wantErr, err)
  627. users, err := db.List(ctx, 1, 10)
  628. require.NoError(t, err)
  629. require.Len(t, users, 3)
  630. }
  631. func usersGetByEmail(t *testing.T, ctx context.Context, db *users) {
  632. t.Run("empty email", func(t *testing.T) {
  633. _, err := db.GetByEmail(ctx, "")
  634. wantErr := ErrUserNotExist{args: errutil.Args{"email": ""}}
  635. assert.Equal(t, wantErr, err)
  636. })
  637. t.Run("ignore organization", func(t *testing.T) {
  638. // TODO: Use Orgs.Create to replace SQL hack when the method is available.
  639. org, err := db.Create(ctx, "gogs", "gogs@exmaple.com", CreateUserOptions{})
  640. require.NoError(t, err)
  641. err = db.Model(&User{}).Where("id", org.ID).UpdateColumn("type", UserTypeOrganization).Error
  642. require.NoError(t, err)
  643. _, err = db.GetByEmail(ctx, org.Email)
  644. wantErr := ErrUserNotExist{args: errutil.Args{"email": org.Email}}
  645. assert.Equal(t, wantErr, err)
  646. })
  647. t.Run("by primary email", func(t *testing.T) {
  648. alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
  649. require.NoError(t, err)
  650. _, err = db.GetByEmail(ctx, alice.Email)
  651. wantErr := ErrUserNotExist{args: errutil.Args{"email": alice.Email}}
  652. assert.Equal(t, wantErr, err)
  653. // Mark user as activated
  654. // TODO: Use UserEmails.Verify to replace SQL hack when the method is available.
  655. err = db.Model(&User{}).Where("id", alice.ID).UpdateColumn("is_active", true).Error
  656. require.NoError(t, err)
  657. user, err := db.GetByEmail(ctx, alice.Email)
  658. require.NoError(t, err)
  659. assert.Equal(t, alice.Name, user.Name)
  660. })
  661. t.Run("by secondary email", func(t *testing.T) {
  662. bob, err := db.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  663. require.NoError(t, err)
  664. // TODO: Use UserEmails.Create to replace SQL hack when the method is available.
  665. email2 := "bob2@exmaple.com"
  666. err = db.Exec(`INSERT INTO email_address (uid, email) VALUES (?, ?)`, bob.ID, email2).Error
  667. require.NoError(t, err)
  668. _, err = db.GetByEmail(ctx, email2)
  669. wantErr := ErrUserNotExist{args: errutil.Args{"email": email2}}
  670. assert.Equal(t, wantErr, err)
  671. // TODO: Use UserEmails.Verify to replace SQL hack when the method is available.
  672. err = db.Exec(`UPDATE email_address SET is_activated = ? WHERE email = ?`, true, email2).Error
  673. require.NoError(t, err)
  674. user, err := db.GetByEmail(ctx, email2)
  675. require.NoError(t, err)
  676. assert.Equal(t, bob.Name, user.Name)
  677. })
  678. }
  679. func usersGetByID(t *testing.T, ctx context.Context, db *users) {
  680. alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
  681. require.NoError(t, err)
  682. user, err := db.GetByID(ctx, alice.ID)
  683. require.NoError(t, err)
  684. assert.Equal(t, alice.Name, user.Name)
  685. _, err = db.GetByID(ctx, 404)
  686. wantErr := ErrUserNotExist{args: errutil.Args{"userID": int64(404)}}
  687. assert.Equal(t, wantErr, err)
  688. }
  689. func usersGetByUsername(t *testing.T, ctx context.Context, db *users) {
  690. t.Run("correct user type", func(t *testing.T) {
  691. alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
  692. require.NoError(t, err)
  693. user, err := db.GetByUsername(ctx, alice.Name)
  694. require.NoError(t, err)
  695. assert.Equal(t, alice.Name, user.Name)
  696. _, err = db.GetByUsername(ctx, "bad_username")
  697. wantErr := ErrUserNotExist{args: errutil.Args{"name": "bad_username"}}
  698. assert.Equal(t, wantErr, err)
  699. })
  700. t.Run("wrong user type", func(t *testing.T) {
  701. // org1,err:=NewOrgsStore(db.DB).Create(ctx,"org1","// TODO: Use Orgs.Create
  702. })
  703. }
  704. func usersGetByKeyID(t *testing.T, ctx context.Context, db *users) {
  705. alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
  706. require.NoError(t, err)
  707. // TODO: Use PublicKeys.Create to replace SQL hack when the method is available.
  708. publicKey := &PublicKey{
  709. OwnerID: alice.ID,
  710. Name: "test-key",
  711. Fingerprint: "12:f8:7e:78:61:b4:bf:e2:de:24:15:96:4e:d4:72:53",
  712. Content: "test-key-content",
  713. CreatedUnix: db.NowFunc().Unix(),
  714. UpdatedUnix: db.NowFunc().Unix(),
  715. }
  716. err = db.WithContext(ctx).Create(publicKey).Error
  717. require.NoError(t, err)
  718. user, err := db.GetByKeyID(ctx, publicKey.ID)
  719. require.NoError(t, err)
  720. assert.Equal(t, alice.Name, user.Name)
  721. _, err = db.GetByKeyID(ctx, publicKey.ID+1)
  722. wantErr := ErrUserNotExist{args: errutil.Args{"keyID": publicKey.ID + 1}}
  723. assert.Equal(t, wantErr, err)
  724. }
  725. func usersGetMailableEmailsByUsernames(t *testing.T, ctx context.Context, db *users) {
  726. alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
  727. require.NoError(t, err)
  728. bob, err := db.Create(ctx, "bob", "bob@exmaple.com", CreateUserOptions{Activated: true})
  729. require.NoError(t, err)
  730. _, err = db.Create(ctx, "cindy", "cindy@exmaple.com", CreateUserOptions{Activated: true})
  731. require.NoError(t, err)
  732. got, err := db.GetMailableEmailsByUsernames(ctx, []string{alice.Name, bob.Name, "ignore-non-exist"})
  733. require.NoError(t, err)
  734. want := []string{bob.Email}
  735. assert.Equal(t, want, got)
  736. }
  737. func usersIsUsernameUsed(t *testing.T, ctx context.Context, db *users) {
  738. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  739. require.NoError(t, err)
  740. tests := []struct {
  741. name string
  742. username string
  743. excludeUserID int64
  744. want bool
  745. }{
  746. {
  747. name: "no change",
  748. username: alice.Name,
  749. excludeUserID: alice.ID,
  750. want: false,
  751. },
  752. {
  753. name: "change case",
  754. username: strings.ToUpper(alice.Name),
  755. excludeUserID: alice.ID,
  756. want: false,
  757. },
  758. {
  759. name: "not used",
  760. username: "bob",
  761. excludeUserID: alice.ID,
  762. want: false,
  763. },
  764. {
  765. name: "not used when not excluded",
  766. username: "bob",
  767. excludeUserID: 0,
  768. want: false,
  769. },
  770. {
  771. name: "used when not excluded",
  772. username: alice.Name,
  773. excludeUserID: 0,
  774. want: true,
  775. },
  776. }
  777. for _, test := range tests {
  778. t.Run(test.name, func(t *testing.T) {
  779. got := db.IsUsernameUsed(ctx, test.username, test.excludeUserID)
  780. assert.Equal(t, test.want, got)
  781. })
  782. }
  783. }
  784. func usersList(t *testing.T, ctx context.Context, db *users) {
  785. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  786. require.NoError(t, err)
  787. bob, err := db.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  788. require.NoError(t, err)
  789. // Create an organization shouldn't count
  790. // TODO: Use Orgs.Create to replace SQL hack when the method is available.
  791. org1, err := db.Create(ctx, "org1", "org1@example.com", CreateUserOptions{})
  792. require.NoError(t, err)
  793. err = db.Exec(
  794. dbutil.Quote("UPDATE %s SET type = ? WHERE id = ?", "user"),
  795. UserTypeOrganization, org1.ID,
  796. ).Error
  797. require.NoError(t, err)
  798. got, err := db.List(ctx, 1, 1)
  799. require.NoError(t, err)
  800. require.Len(t, got, 1)
  801. assert.Equal(t, alice.ID, got[0].ID)
  802. got, err = db.List(ctx, 2, 1)
  803. require.NoError(t, err)
  804. require.Len(t, got, 1)
  805. assert.Equal(t, bob.ID, got[0].ID)
  806. got, err = db.List(ctx, 1, 3)
  807. require.NoError(t, err)
  808. require.Len(t, got, 2)
  809. assert.Equal(t, alice.ID, got[0].ID)
  810. assert.Equal(t, bob.ID, got[1].ID)
  811. }
  812. func usersListFollowers(t *testing.T, ctx context.Context, db *users) {
  813. john, err := db.Create(ctx, "john", "john@example.com", CreateUserOptions{})
  814. require.NoError(t, err)
  815. got, err := db.ListFollowers(ctx, john.ID, 1, 1)
  816. require.NoError(t, err)
  817. assert.Empty(t, got)
  818. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  819. require.NoError(t, err)
  820. bob, err := db.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  821. require.NoError(t, err)
  822. err = db.Follow(ctx, alice.ID, john.ID)
  823. require.NoError(t, err)
  824. err = db.Follow(ctx, bob.ID, john.ID)
  825. require.NoError(t, err)
  826. // First page only has bob
  827. got, err = db.ListFollowers(ctx, john.ID, 1, 1)
  828. require.NoError(t, err)
  829. require.Len(t, got, 1)
  830. assert.Equal(t, bob.ID, got[0].ID)
  831. // Second page only has alice
  832. got, err = db.ListFollowers(ctx, john.ID, 2, 1)
  833. require.NoError(t, err)
  834. require.Len(t, got, 1)
  835. assert.Equal(t, alice.ID, got[0].ID)
  836. }
  837. func usersListFollowings(t *testing.T, ctx context.Context, db *users) {
  838. john, err := db.Create(ctx, "john", "john@example.com", CreateUserOptions{})
  839. require.NoError(t, err)
  840. got, err := db.ListFollowers(ctx, john.ID, 1, 1)
  841. require.NoError(t, err)
  842. assert.Empty(t, got)
  843. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  844. require.NoError(t, err)
  845. bob, err := db.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  846. require.NoError(t, err)
  847. err = db.Follow(ctx, john.ID, alice.ID)
  848. require.NoError(t, err)
  849. err = db.Follow(ctx, john.ID, bob.ID)
  850. require.NoError(t, err)
  851. // First page only has bob
  852. got, err = db.ListFollowings(ctx, john.ID, 1, 1)
  853. require.NoError(t, err)
  854. require.Len(t, got, 1)
  855. assert.Equal(t, bob.ID, got[0].ID)
  856. // Second page only has alice
  857. got, err = db.ListFollowings(ctx, john.ID, 2, 1)
  858. require.NoError(t, err)
  859. require.Len(t, got, 1)
  860. assert.Equal(t, alice.ID, got[0].ID)
  861. }
  862. func usersSearchByName(t *testing.T, ctx context.Context, db *users) {
  863. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{FullName: "Alice Jordan"})
  864. require.NoError(t, err)
  865. bob, err := db.Create(ctx, "bob", "bob@example.com", CreateUserOptions{FullName: "Bob Jordan"})
  866. require.NoError(t, err)
  867. t.Run("search for username alice", func(t *testing.T) {
  868. users, count, err := db.SearchByName(ctx, "Li", 1, 1, "")
  869. require.NoError(t, err)
  870. require.Len(t, users, int(count))
  871. assert.Equal(t, int64(1), count)
  872. assert.Equal(t, alice.ID, users[0].ID)
  873. })
  874. t.Run("search for username bob", func(t *testing.T) {
  875. users, count, err := db.SearchByName(ctx, "oB", 1, 1, "")
  876. require.NoError(t, err)
  877. require.Len(t, users, int(count))
  878. assert.Equal(t, int64(1), count)
  879. assert.Equal(t, bob.ID, users[0].ID)
  880. })
  881. t.Run("search for full name jordan", func(t *testing.T) {
  882. users, count, err := db.SearchByName(ctx, "Jo", 1, 10, "")
  883. require.NoError(t, err)
  884. require.Len(t, users, int(count))
  885. assert.Equal(t, int64(2), count)
  886. })
  887. t.Run("search for full name jordan ORDER BY id DESC LIMIT 1", func(t *testing.T) {
  888. users, count, err := db.SearchByName(ctx, "Jo", 1, 1, "id DESC")
  889. require.NoError(t, err)
  890. require.Len(t, users, 1)
  891. assert.Equal(t, int64(2), count)
  892. assert.Equal(t, bob.ID, users[0].ID)
  893. })
  894. }
  895. func usersUpdate(t *testing.T, ctx context.Context, db *users) {
  896. const oldPassword = "Password"
  897. alice, err := db.Create(
  898. ctx,
  899. "alice",
  900. "alice@example.com",
  901. CreateUserOptions{
  902. FullName: "FullName",
  903. Password: oldPassword,
  904. LoginSource: 9,
  905. LoginName: "LoginName",
  906. Location: "Location",
  907. Website: "Website",
  908. Activated: false,
  909. Admin: false,
  910. },
  911. )
  912. require.NoError(t, err)
  913. t.Run("update password", func(t *testing.T) {
  914. got := userutil.ValidatePassword(alice.Password, alice.Salt, oldPassword)
  915. require.True(t, got)
  916. newPassword := "NewPassword"
  917. err = db.Update(ctx, alice.ID, UpdateUserOptions{Password: &newPassword})
  918. require.NoError(t, err)
  919. alice, err = db.GetByID(ctx, alice.ID)
  920. require.NoError(t, err)
  921. got = userutil.ValidatePassword(alice.Password, alice.Salt, oldPassword)
  922. assert.False(t, got, "Old password should stop working")
  923. got = userutil.ValidatePassword(alice.Password, alice.Salt, newPassword)
  924. assert.True(t, got, "New password should work")
  925. })
  926. t.Run("update email but already used", func(t *testing.T) {
  927. bob, err := db.Create(
  928. ctx,
  929. "bob",
  930. "bob@example.com",
  931. CreateUserOptions{
  932. Activated: true,
  933. },
  934. )
  935. require.NoError(t, err)
  936. got := db.Update(ctx, alice.ID, UpdateUserOptions{Email: &bob.Email})
  937. want := ErrEmailAlreadyUsed{args: errutil.Args{"email": bob.Email}}
  938. assert.Equal(t, want, got)
  939. })
  940. loginSource := int64(1)
  941. maxRepoCreation := 99
  942. lastRepoVisibility := true
  943. overLimitStr := strings.Repeat("a", 2050)
  944. opts := UpdateUserOptions{
  945. LoginSource: &loginSource,
  946. LoginName: &alice.Name,
  947. FullName: &overLimitStr,
  948. Website: &overLimitStr,
  949. Location: &overLimitStr,
  950. Description: &overLimitStr,
  951. MaxRepoCreation: &maxRepoCreation,
  952. LastRepoVisibility: &lastRepoVisibility,
  953. IsActivated: &lastRepoVisibility,
  954. IsAdmin: &lastRepoVisibility,
  955. AllowGitHook: &lastRepoVisibility,
  956. AllowImportLocal: &lastRepoVisibility,
  957. ProhibitLogin: &lastRepoVisibility,
  958. Avatar: &overLimitStr,
  959. AvatarEmail: &overLimitStr,
  960. }
  961. err = db.Update(ctx, alice.ID, opts)
  962. require.NoError(t, err)
  963. alice, err = db.GetByID(ctx, alice.ID)
  964. require.NoError(t, err)
  965. assertValues := func() {
  966. assert.Equal(t, loginSource, alice.LoginSource)
  967. assert.Equal(t, alice.Name, alice.LoginName)
  968. wantStr255 := strings.Repeat("a", 255)
  969. assert.Equal(t, wantStr255, alice.FullName)
  970. assert.Equal(t, wantStr255, alice.Website)
  971. assert.Equal(t, wantStr255, alice.Location)
  972. assert.Equal(t, wantStr255, alice.Description)
  973. assert.Equal(t, maxRepoCreation, alice.MaxRepoCreation)
  974. assert.Equal(t, lastRepoVisibility, alice.LastRepoVisibility)
  975. assert.Equal(t, lastRepoVisibility, alice.IsActive)
  976. assert.Equal(t, lastRepoVisibility, alice.IsAdmin)
  977. assert.Equal(t, lastRepoVisibility, alice.AllowGitHook)
  978. assert.Equal(t, lastRepoVisibility, alice.AllowImportLocal)
  979. assert.Equal(t, lastRepoVisibility, alice.ProhibitLogin)
  980. wantStr2048 := strings.Repeat("a", 2048)
  981. assert.Equal(t, wantStr2048, alice.Avatar)
  982. assert.Equal(t, wantStr255, alice.AvatarEmail)
  983. }
  984. assertValues()
  985. // Test ignored values
  986. err = db.Update(ctx, alice.ID, UpdateUserOptions{})
  987. require.NoError(t, err)
  988. alice, err = db.GetByID(ctx, alice.ID)
  989. require.NoError(t, err)
  990. assertValues()
  991. }
  992. func usersUseCustomAvatar(t *testing.T, ctx context.Context, db *users) {
  993. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  994. require.NoError(t, err)
  995. avatar, err := public.Files.ReadFile("img/avatar_default.png")
  996. require.NoError(t, err)
  997. avatarPath := userutil.CustomAvatarPath(alice.ID)
  998. _ = os.Remove(avatarPath)
  999. defer func() { _ = os.Remove(avatarPath) }()
  1000. err = db.UseCustomAvatar(ctx, alice.ID, avatar)
  1001. require.NoError(t, err)
  1002. // Make sure avatar is saved and the user flag is updated.
  1003. got := osutil.IsFile(avatarPath)
  1004. assert.True(t, got)
  1005. alice, err = db.GetByID(ctx, alice.ID)
  1006. require.NoError(t, err)
  1007. assert.True(t, alice.UseCustomAvatar)
  1008. }
  1009. func TestIsUsernameAllowed(t *testing.T) {
  1010. for name := range reservedUsernames {
  1011. t.Run(name, func(t *testing.T) {
  1012. assert.True(t, IsErrNameNotAllowed(isUsernameAllowed(name)))
  1013. })
  1014. }
  1015. for _, pattern := range reservedUsernamePatterns {
  1016. t.Run(pattern, func(t *testing.T) {
  1017. username := strings.ReplaceAll(pattern, "*", "alice")
  1018. assert.True(t, IsErrNameNotAllowed(isUsernameAllowed(username)))
  1019. })
  1020. }
  1021. }
  1022. func usersAddEmail(t *testing.T, ctx context.Context, db *users) {
  1023. t.Run("multiple users can add the same unverified email", func(t *testing.T) {
  1024. alice, err := db.Create(ctx, "alice", "unverified@example.com", CreateUserOptions{})
  1025. require.NoError(t, err)
  1026. err = db.AddEmail(ctx, alice.ID+1, "unverified@example.com", false)
  1027. require.NoError(t, err)
  1028. })
  1029. t.Run("only one user can add the same verified email", func(t *testing.T) {
  1030. bob, err := db.Create(ctx, "bob", "verified@example.com", CreateUserOptions{Activated: true})
  1031. require.NoError(t, err)
  1032. got := db.AddEmail(ctx, bob.ID+1, "verified@example.com", true)
  1033. want := ErrEmailAlreadyUsed{args: errutil.Args{"email": "verified@example.com"}}
  1034. require.Equal(t, want, got)
  1035. })
  1036. }
  1037. func usersGetEmail(t *testing.T, ctx context.Context, db *users) {
  1038. const testUserID = 1
  1039. const testEmail = "alice@example.com"
  1040. _, err := db.GetEmail(ctx, testUserID, testEmail, false)
  1041. wantErr := ErrEmailNotExist{
  1042. args: errutil.Args{
  1043. "email": testEmail,
  1044. },
  1045. }
  1046. assert.Equal(t, wantErr, err)
  1047. err = db.AddEmail(ctx, testUserID, testEmail, false)
  1048. require.NoError(t, err)
  1049. got, err := db.GetEmail(ctx, testUserID, testEmail, false)
  1050. require.NoError(t, err)
  1051. assert.Equal(t, testEmail, got.Email)
  1052. // Should not return if we ask for a different user
  1053. _, err = db.GetEmail(ctx, testUserID+1, testEmail, false)
  1054. assert.Equal(t, wantErr, err)
  1055. // Should not return if we only want activated emails
  1056. _, err = db.GetEmail(ctx, testUserID, testEmail, true)
  1057. assert.Equal(t, wantErr, err)
  1058. err = db.MarkEmailActivated(ctx, testUserID, testEmail)
  1059. require.NoError(t, err)
  1060. got, err = db.GetEmail(ctx, testUserID, testEmail, true)
  1061. require.NoError(t, err)
  1062. assert.Equal(t, testEmail, got.Email)
  1063. }
  1064. func usersListEmails(t *testing.T, ctx context.Context, db *users) {
  1065. t.Run("list emails with primary email", func(t *testing.T) {
  1066. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  1067. require.NoError(t, err)
  1068. err = db.AddEmail(ctx, alice.ID, "alice2@example.com", true)
  1069. require.NoError(t, err)
  1070. err = db.MarkEmailPrimary(ctx, alice.ID, "alice2@example.com")
  1071. require.NoError(t, err)
  1072. emails, err := db.ListEmails(ctx, alice.ID)
  1073. require.NoError(t, err)
  1074. got := make([]string, 0, len(emails))
  1075. for _, email := range emails {
  1076. got = append(got, email.Email)
  1077. }
  1078. want := []string{"alice2@example.com", "alice@example.com"}
  1079. assert.Equal(t, want, got)
  1080. })
  1081. t.Run("list emails without primary email", func(t *testing.T) {
  1082. bob, err := db.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  1083. require.NoError(t, err)
  1084. err = db.AddEmail(ctx, bob.ID, "bob2@example.com", false)
  1085. require.NoError(t, err)
  1086. emails, err := db.ListEmails(ctx, bob.ID)
  1087. require.NoError(t, err)
  1088. got := make([]string, 0, len(emails))
  1089. for _, email := range emails {
  1090. got = append(got, email.Email)
  1091. }
  1092. want := []string{"bob2@example.com", "bob@example.com"}
  1093. assert.Equal(t, want, got)
  1094. })
  1095. }
  1096. func usersMarkEmailActivated(t *testing.T, ctx context.Context, db *users) {
  1097. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  1098. require.NoError(t, err)
  1099. err = db.AddEmail(ctx, alice.ID, "alice2@example.com", false)
  1100. require.NoError(t, err)
  1101. err = db.MarkEmailActivated(ctx, alice.ID, "alice2@example.com")
  1102. require.NoError(t, err)
  1103. gotEmail, err := db.GetEmail(ctx, alice.ID, "alice2@example.com", true)
  1104. require.NoError(t, err)
  1105. assert.True(t, gotEmail.IsActivated)
  1106. gotAlice, err := db.GetByID(ctx, alice.ID)
  1107. require.NoError(t, err)
  1108. assert.NotEqual(t, alice.Rands, gotAlice.Rands)
  1109. }
  1110. func usersMarkEmailPrimary(t *testing.T, ctx context.Context, db *users) {
  1111. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  1112. require.NoError(t, err)
  1113. err = db.AddEmail(ctx, alice.ID, "alice2@example.com", false)
  1114. require.NoError(t, err)
  1115. // Should fail because email not verified
  1116. gotError := db.MarkEmailPrimary(ctx, alice.ID, "alice2@example.com")
  1117. wantError := ErrEmailNotVerified{args: errutil.Args{"email": "alice2@example.com"}}
  1118. assert.Equal(t, wantError, gotError)
  1119. // Mark email as verified and should succeed
  1120. err = db.MarkEmailActivated(ctx, alice.ID, "alice2@example.com")
  1121. require.NoError(t, err)
  1122. err = db.MarkEmailPrimary(ctx, alice.ID, "alice2@example.com")
  1123. require.NoError(t, err)
  1124. gotAlice, err := db.GetByID(ctx, alice.ID)
  1125. require.NoError(t, err)
  1126. assert.Equal(t, "alice2@example.com", gotAlice.Email)
  1127. // Former primary email should be preserved
  1128. gotEmail, err := db.GetEmail(ctx, alice.ID, "alice@example.com", false)
  1129. require.NoError(t, err)
  1130. assert.False(t, gotEmail.IsActivated)
  1131. }
  1132. func usersDeleteEmail(t *testing.T, ctx context.Context, db *users) {
  1133. alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  1134. require.NoError(t, err)
  1135. err = db.AddEmail(ctx, alice.ID, "alice2@example.com", false)
  1136. require.NoError(t, err)
  1137. _, err = db.GetEmail(ctx, alice.ID, "alice2@example.com", false)
  1138. require.NoError(t, err)
  1139. err = db.DeleteEmail(ctx, alice.ID, "alice2@example.com")
  1140. require.NoError(t, err)
  1141. _, got := db.GetEmail(ctx, alice.ID, "alice2@example.com", false)
  1142. want := ErrEmailNotExist{args: errutil.Args{"email": "alice2@example.com"}}
  1143. require.Equal(t, want, got)
  1144. }
  1145. func usersFollow(t *testing.T, ctx context.Context, db *users) {
  1146. usersStore := NewUsersStore(db.DB)
  1147. alice, err := usersStore.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  1148. require.NoError(t, err)
  1149. bob, err := usersStore.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  1150. require.NoError(t, err)
  1151. err = db.Follow(ctx, alice.ID, bob.ID)
  1152. require.NoError(t, err)
  1153. // It is OK to follow multiple times and just be noop.
  1154. err = db.Follow(ctx, alice.ID, bob.ID)
  1155. require.NoError(t, err)
  1156. alice, err = usersStore.GetByID(ctx, alice.ID)
  1157. require.NoError(t, err)
  1158. assert.Equal(t, 1, alice.NumFollowing)
  1159. bob, err = usersStore.GetByID(ctx, bob.ID)
  1160. require.NoError(t, err)
  1161. assert.Equal(t, 1, bob.NumFollowers)
  1162. }
  1163. func usersIsFollowing(t *testing.T, ctx context.Context, db *users) {
  1164. usersStore := NewUsersStore(db.DB)
  1165. alice, err := usersStore.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  1166. require.NoError(t, err)
  1167. bob, err := usersStore.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  1168. require.NoError(t, err)
  1169. got := db.IsFollowing(ctx, alice.ID, bob.ID)
  1170. assert.False(t, got)
  1171. err = db.Follow(ctx, alice.ID, bob.ID)
  1172. require.NoError(t, err)
  1173. got = db.IsFollowing(ctx, alice.ID, bob.ID)
  1174. assert.True(t, got)
  1175. err = db.Unfollow(ctx, alice.ID, bob.ID)
  1176. require.NoError(t, err)
  1177. got = db.IsFollowing(ctx, alice.ID, bob.ID)
  1178. assert.False(t, got)
  1179. }
  1180. func usersUnfollow(t *testing.T, ctx context.Context, db *users) {
  1181. usersStore := NewUsersStore(db.DB)
  1182. alice, err := usersStore.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  1183. require.NoError(t, err)
  1184. bob, err := usersStore.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  1185. require.NoError(t, err)
  1186. err = db.Follow(ctx, alice.ID, bob.ID)
  1187. require.NoError(t, err)
  1188. // It is OK to unfollow multiple times and just be noop.
  1189. err = db.Unfollow(ctx, alice.ID, bob.ID)
  1190. require.NoError(t, err)
  1191. err = db.Unfollow(ctx, alice.ID, bob.ID)
  1192. require.NoError(t, err)
  1193. alice, err = usersStore.GetByID(ctx, alice.ID)
  1194. require.NoError(t, err)
  1195. assert.Equal(t, 0, alice.NumFollowing)
  1196. bob, err = usersStore.GetByID(ctx, bob.ID)
  1197. require.NoError(t, err)
  1198. assert.Equal(t, 0, bob.NumFollowers)
  1199. }