manager_test.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. package user
  2. import (
  3. "database/sql"
  4. "github.com/stretchr/testify/require"
  5. "path/filepath"
  6. "strings"
  7. "testing"
  8. "time"
  9. )
  10. const minBcryptTimingMillis = int64(50) // Ideally should be >100ms, but this should also run on a Raspberry Pi without massive resources
  11. func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
  12. a := newTestManager(t, PermissionDenyAll)
  13. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, "unit-test"))
  14. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  15. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  16. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  17. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  18. require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
  19. require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
  20. require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
  21. require.Nil(t, a.AllowAccess(Everyone, "up*", PermissionWrite)) // Everyone can write to /up*
  22. phil, err := a.Authenticate("phil", "phil")
  23. require.Nil(t, err)
  24. require.Equal(t, "phil", phil.Name)
  25. require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
  26. require.Equal(t, RoleAdmin, phil.Role)
  27. philGrants, err := a.Grants("phil")
  28. require.Nil(t, err)
  29. require.Equal(t, []Grant{}, philGrants)
  30. ben, err := a.Authenticate("ben", "ben")
  31. require.Nil(t, err)
  32. require.Equal(t, "ben", ben.Name)
  33. require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
  34. require.Equal(t, RoleUser, ben.Role)
  35. benGrants, err := a.Grants("ben")
  36. require.Nil(t, err)
  37. require.Equal(t, []Grant{
  38. {"mytopic", PermissionReadWrite},
  39. {"writeme", PermissionWrite},
  40. {"readme", PermissionRead},
  41. {"everyonewrite", PermissionDenyAll},
  42. }, benGrants)
  43. notben, err := a.Authenticate("ben", "this is wrong")
  44. require.Nil(t, notben)
  45. require.Equal(t, ErrUnauthenticated, err)
  46. // Admin can do everything
  47. require.Nil(t, a.Authorize(phil, "sometopic", PermissionWrite))
  48. require.Nil(t, a.Authorize(phil, "mytopic", PermissionRead))
  49. require.Nil(t, a.Authorize(phil, "readme", PermissionWrite))
  50. require.Nil(t, a.Authorize(phil, "writeme", PermissionWrite))
  51. require.Nil(t, a.Authorize(phil, "announcements", PermissionWrite))
  52. require.Nil(t, a.Authorize(phil, "everyonewrite", PermissionWrite))
  53. // User cannot do everything
  54. require.Nil(t, a.Authorize(ben, "mytopic", PermissionWrite))
  55. require.Nil(t, a.Authorize(ben, "mytopic", PermissionRead))
  56. require.Nil(t, a.Authorize(ben, "readme", PermissionRead))
  57. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "readme", PermissionWrite))
  58. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "writeme", PermissionRead))
  59. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
  60. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
  61. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "everyonewrite", PermissionRead))
  62. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "everyonewrite", PermissionWrite))
  63. require.Nil(t, a.Authorize(ben, "announcements", PermissionRead))
  64. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "announcements", PermissionWrite))
  65. // Everyone else can do barely anything
  66. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionRead))
  67. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionWrite))
  68. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopic", PermissionRead))
  69. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopic", PermissionWrite))
  70. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "readme", PermissionRead))
  71. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "readme", PermissionWrite))
  72. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "writeme", PermissionRead))
  73. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "writeme", PermissionWrite))
  74. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "announcements", PermissionWrite))
  75. require.Nil(t, a.Authorize(nil, "announcements", PermissionRead))
  76. require.Nil(t, a.Authorize(nil, "everyonewrite", PermissionRead))
  77. require.Nil(t, a.Authorize(nil, "everyonewrite", PermissionWrite))
  78. require.Nil(t, a.Authorize(nil, "up1234", PermissionWrite)) // Wildcard permission
  79. require.Nil(t, a.Authorize(nil, "up5678", PermissionWrite))
  80. }
  81. func TestManager_AddUser_Invalid(t *testing.T) {
  82. a := newTestManager(t, PermissionDenyAll)
  83. require.Equal(t, ErrInvalidArgument, a.AddUser(" invalid ", "pass", RoleAdmin, "unit-test"))
  84. require.Equal(t, ErrInvalidArgument, a.AddUser("validuser", "pass", "invalid-role", "unit-test"))
  85. }
  86. func TestManager_AddUser_Timing(t *testing.T) {
  87. a := newTestManager(t, PermissionDenyAll)
  88. start := time.Now().UnixMilli()
  89. require.Nil(t, a.AddUser("user", "pass", RoleAdmin, "unit-test"))
  90. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  91. }
  92. func TestManager_Authenticate_Timing(t *testing.T) {
  93. a := newTestManager(t, PermissionDenyAll)
  94. require.Nil(t, a.AddUser("user", "pass", RoleAdmin, "unit-test"))
  95. // Timing a correct attempt
  96. start := time.Now().UnixMilli()
  97. _, err := a.Authenticate("user", "pass")
  98. require.Nil(t, err)
  99. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  100. // Timing an incorrect attempt
  101. start = time.Now().UnixMilli()
  102. _, err = a.Authenticate("user", "INCORRECT")
  103. require.Equal(t, ErrUnauthenticated, err)
  104. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  105. // Timing a non-existing user attempt
  106. start = time.Now().UnixMilli()
  107. _, err = a.Authenticate("DOES-NOT-EXIST", "hithere")
  108. require.Equal(t, ErrUnauthenticated, err)
  109. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  110. }
  111. func TestManager_UserManagement(t *testing.T) {
  112. a := newTestManager(t, PermissionDenyAll)
  113. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, "unit-test"))
  114. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  115. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  116. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  117. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  118. require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
  119. require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
  120. require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
  121. // Query user details
  122. phil, err := a.User("phil")
  123. require.Nil(t, err)
  124. require.Equal(t, "phil", phil.Name)
  125. require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
  126. require.Equal(t, RoleAdmin, phil.Role)
  127. philGrants, err := a.Grants("phil")
  128. require.Nil(t, err)
  129. require.Equal(t, []Grant{}, philGrants)
  130. ben, err := a.User("ben")
  131. require.Nil(t, err)
  132. require.Equal(t, "ben", ben.Name)
  133. require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
  134. require.Equal(t, RoleUser, ben.Role)
  135. benGrants, err := a.Grants("ben")
  136. require.Nil(t, err)
  137. require.Equal(t, []Grant{
  138. {"mytopic", PermissionReadWrite},
  139. {"writeme", PermissionWrite},
  140. {"readme", PermissionRead},
  141. {"everyonewrite", PermissionDenyAll},
  142. }, benGrants)
  143. everyone, err := a.User(Everyone)
  144. require.Nil(t, err)
  145. require.Equal(t, "*", everyone.Name)
  146. require.Equal(t, "", everyone.Hash)
  147. require.Equal(t, RoleAnonymous, everyone.Role)
  148. everyoneGrants, err := a.Grants(Everyone)
  149. require.Nil(t, err)
  150. require.Equal(t, []Grant{
  151. {"everyonewrite", PermissionReadWrite},
  152. {"announcements", PermissionRead},
  153. }, everyoneGrants)
  154. // Ben: Before revoking
  155. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite)) // Overwrite!
  156. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  157. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  158. require.Nil(t, a.Authorize(ben, "mytopic", PermissionRead))
  159. require.Nil(t, a.Authorize(ben, "mytopic", PermissionWrite))
  160. require.Nil(t, a.Authorize(ben, "readme", PermissionRead))
  161. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
  162. // Revoke access for "ben" to "mytopic", then check again
  163. require.Nil(t, a.ResetAccess("ben", "mytopic"))
  164. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionWrite)) // Revoked
  165. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionRead)) // Revoked
  166. require.Nil(t, a.Authorize(ben, "readme", PermissionRead)) // Unchanged
  167. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite)) // Unchanged
  168. // Revoke rest of the access
  169. require.Nil(t, a.ResetAccess("ben", ""))
  170. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "readme", PermissionRead)) // Revoked
  171. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "wrtiteme", PermissionWrite)) // Revoked
  172. // User list
  173. users, err := a.Users()
  174. require.Nil(t, err)
  175. require.Equal(t, 3, len(users))
  176. require.Equal(t, "phil", users[0].Name)
  177. require.Equal(t, "ben", users[1].Name)
  178. require.Equal(t, "*", users[2].Name)
  179. // Remove user
  180. require.Nil(t, a.RemoveUser("ben"))
  181. _, err = a.User("ben")
  182. require.Equal(t, ErrUserNotFound, err)
  183. users, err = a.Users()
  184. require.Nil(t, err)
  185. require.Equal(t, 2, len(users))
  186. require.Equal(t, "phil", users[0].Name)
  187. require.Equal(t, "*", users[1].Name)
  188. }
  189. func TestManager_ChangePassword(t *testing.T) {
  190. a := newTestManager(t, PermissionDenyAll)
  191. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, "unit-test"))
  192. _, err := a.Authenticate("phil", "phil")
  193. require.Nil(t, err)
  194. require.Nil(t, a.ChangePassword("phil", "newpass"))
  195. _, err = a.Authenticate("phil", "phil")
  196. require.Equal(t, ErrUnauthenticated, err)
  197. _, err = a.Authenticate("phil", "newpass")
  198. require.Nil(t, err)
  199. }
  200. func TestManager_ChangeRole(t *testing.T) {
  201. a := newTestManager(t, PermissionDenyAll)
  202. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  203. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  204. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  205. ben, err := a.User("ben")
  206. require.Nil(t, err)
  207. require.Equal(t, RoleUser, ben.Role)
  208. benGrants, err := a.Grants("ben")
  209. require.Nil(t, err)
  210. require.Equal(t, 2, len(benGrants))
  211. require.Nil(t, a.ChangeRole("ben", RoleAdmin))
  212. ben, err = a.User("ben")
  213. require.Nil(t, err)
  214. require.Equal(t, RoleAdmin, ben.Role)
  215. benGrants, err = a.Grants("ben")
  216. require.Nil(t, err)
  217. require.Equal(t, 0, len(benGrants))
  218. }
  219. func TestManager_Reservations(t *testing.T) {
  220. a := newTestManager(t, PermissionDenyAll)
  221. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  222. require.Nil(t, a.ReserveAccess("ben", "ztopic", PermissionDenyAll))
  223. require.Nil(t, a.ReserveAccess("ben", "readme", PermissionRead))
  224. require.Nil(t, a.AllowAccess("ben", "something-else", PermissionRead))
  225. reservations, err := a.Reservations("ben")
  226. require.Nil(t, err)
  227. require.Equal(t, 2, len(reservations))
  228. require.Equal(t, Reservation{
  229. Topic: "readme",
  230. Owner: PermissionReadWrite,
  231. Everyone: PermissionRead,
  232. }, reservations[0])
  233. require.Equal(t, Reservation{
  234. Topic: "ztopic",
  235. Owner: PermissionReadWrite,
  236. Everyone: PermissionDenyAll,
  237. }, reservations[1])
  238. }
  239. func TestManager_ChangeRoleFromTierUserToAdmin(t *testing.T) {
  240. a := newTestManager(t, PermissionDenyAll)
  241. require.Nil(t, a.CreateTier(&Tier{
  242. Code: "pro",
  243. Name: "ntfy Pro",
  244. StripePriceID: "price123",
  245. MessagesLimit: 5_000,
  246. MessagesExpiryDuration: 3 * 24 * time.Hour,
  247. EmailsLimit: 50,
  248. ReservationsLimit: 5,
  249. AttachmentFileSizeLimit: 52428800,
  250. AttachmentTotalSizeLimit: 524288000,
  251. AttachmentExpiryDuration: 24 * time.Hour,
  252. }))
  253. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  254. require.Nil(t, a.ChangeTier("ben", "pro"))
  255. require.Nil(t, a.ReserveAccess("ben", "mytopic", PermissionDenyAll))
  256. ben, err := a.User("ben")
  257. require.Nil(t, err)
  258. require.Equal(t, RoleUser, ben.Role)
  259. require.Equal(t, "pro", ben.Tier.Code)
  260. require.Equal(t, true, ben.Tier.Paid)
  261. require.Equal(t, int64(5000), ben.Tier.MessagesLimit)
  262. require.Equal(t, 3*24*time.Hour, ben.Tier.MessagesExpiryDuration)
  263. require.Equal(t, int64(50), ben.Tier.EmailsLimit)
  264. require.Equal(t, int64(5), ben.Tier.ReservationsLimit)
  265. require.Equal(t, int64(52428800), ben.Tier.AttachmentFileSizeLimit)
  266. require.Equal(t, int64(524288000), ben.Tier.AttachmentTotalSizeLimit)
  267. require.Equal(t, 24*time.Hour, ben.Tier.AttachmentExpiryDuration)
  268. benGrants, err := a.Grants("ben")
  269. require.Nil(t, err)
  270. require.Equal(t, 1, len(benGrants))
  271. require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
  272. everyoneGrants, err := a.Grants(Everyone)
  273. require.Nil(t, err)
  274. require.Equal(t, 1, len(everyoneGrants))
  275. require.Equal(t, PermissionDenyAll, everyoneGrants[0].Allow)
  276. benReservations, err := a.Reservations("ben")
  277. require.Nil(t, err)
  278. require.Equal(t, 1, len(benReservations))
  279. require.Equal(t, "mytopic", benReservations[0].Topic)
  280. require.Equal(t, PermissionReadWrite, benReservations[0].Owner)
  281. require.Equal(t, PermissionDenyAll, benReservations[0].Everyone)
  282. // Switch to admin, this should remove all grants and owned ACL entries
  283. require.Nil(t, a.ChangeRole("ben", RoleAdmin))
  284. benGrants, err = a.Grants("ben")
  285. require.Nil(t, err)
  286. require.Equal(t, 0, len(benGrants))
  287. everyoneGrants, err = a.Grants(Everyone)
  288. require.Nil(t, err)
  289. require.Equal(t, 0, len(everyoneGrants))
  290. }
  291. func TestManager_Token_Valid(t *testing.T) {
  292. a := newTestManager(t, PermissionDenyAll)
  293. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  294. u, err := a.User("ben")
  295. require.Nil(t, err)
  296. // Create token for user
  297. token, err := a.CreateToken(u)
  298. require.Nil(t, err)
  299. require.NotEmpty(t, token.Value)
  300. require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires.Unix())
  301. u2, err := a.AuthenticateToken(token.Value)
  302. require.Nil(t, err)
  303. require.Equal(t, u.Name, u2.Name)
  304. require.Equal(t, token.Value, u2.Token)
  305. // Remove token and auth again
  306. require.Nil(t, a.RemoveToken(u2))
  307. u3, err := a.AuthenticateToken(token.Value)
  308. require.Equal(t, ErrUnauthenticated, err)
  309. require.Nil(t, u3)
  310. }
  311. func TestManager_Token_Invalid(t *testing.T) {
  312. a := newTestManager(t, PermissionDenyAll)
  313. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  314. u, err := a.AuthenticateToken(strings.Repeat("x", 32)) // 32 == token length
  315. require.Nil(t, u)
  316. require.Equal(t, ErrUnauthenticated, err)
  317. u, err = a.AuthenticateToken("not long enough anyway")
  318. require.Nil(t, u)
  319. require.Equal(t, ErrUnauthenticated, err)
  320. }
  321. func TestManager_Token_Expire(t *testing.T) {
  322. a := newTestManager(t, PermissionDenyAll)
  323. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  324. u, err := a.User("ben")
  325. require.Nil(t, err)
  326. // Create tokens for user
  327. token1, err := a.CreateToken(u)
  328. require.Nil(t, err)
  329. require.NotEmpty(t, token1.Value)
  330. require.True(t, time.Now().Add(71*time.Hour).Unix() < token1.Expires.Unix())
  331. token2, err := a.CreateToken(u)
  332. require.Nil(t, err)
  333. require.NotEmpty(t, token2.Value)
  334. require.NotEqual(t, token1.Value, token2.Value)
  335. require.True(t, time.Now().Add(71*time.Hour).Unix() < token2.Expires.Unix())
  336. // See that tokens work
  337. _, err = a.AuthenticateToken(token1.Value)
  338. require.Nil(t, err)
  339. _, err = a.AuthenticateToken(token2.Value)
  340. require.Nil(t, err)
  341. // Modify token expiration in database
  342. _, err = a.db.Exec("UPDATE user_token SET expires = 1 WHERE token = ?", token1.Value)
  343. require.Nil(t, err)
  344. // Now token1 shouldn't work anymore
  345. _, err = a.AuthenticateToken(token1.Value)
  346. require.Equal(t, ErrUnauthenticated, err)
  347. result, err := a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
  348. require.Nil(t, err)
  349. require.True(t, result.Next()) // Still a matching row
  350. require.Nil(t, result.Close())
  351. // Expire tokens and check database rows
  352. require.Nil(t, a.RemoveExpiredTokens())
  353. result, err = a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
  354. require.Nil(t, err)
  355. require.False(t, result.Next()) // No matching row!
  356. require.Nil(t, result.Close())
  357. }
  358. func TestManager_Token_Extend(t *testing.T) {
  359. a := newTestManager(t, PermissionDenyAll)
  360. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  361. // Try to extend token for user without token
  362. u, err := a.User("ben")
  363. require.Nil(t, err)
  364. _, err = a.ExtendToken(u)
  365. require.Equal(t, errNoTokenProvided, err)
  366. // Create token for user
  367. token, err := a.CreateToken(u)
  368. require.Nil(t, err)
  369. require.NotEmpty(t, token.Value)
  370. userWithToken, err := a.AuthenticateToken(token.Value)
  371. require.Nil(t, err)
  372. time.Sleep(1100 * time.Millisecond)
  373. extendedToken, err := a.ExtendToken(userWithToken)
  374. require.Nil(t, err)
  375. require.Equal(t, token.Value, extendedToken.Value)
  376. require.True(t, token.Expires.Unix() < extendedToken.Expires.Unix())
  377. }
  378. func TestManager_Token_MaxCount_AutoDelete(t *testing.T) {
  379. a := newTestManager(t, PermissionDenyAll)
  380. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  381. // Try to extend token for user without token
  382. u, err := a.User("ben")
  383. require.Nil(t, err)
  384. // Tokens
  385. baseTime := time.Now().Add(24 * time.Hour)
  386. tokens := make([]string, 0)
  387. for i := 0; i < 12; i++ {
  388. token, err := a.CreateToken(u)
  389. require.Nil(t, err)
  390. require.NotEmpty(t, token.Value)
  391. tokens = append(tokens, token.Value)
  392. // Manually modify expiry date to avoid sorting issues (this is a hack)
  393. _, err = a.db.Exec(`UPDATE user_token SET expires=? WHERE token=?`, baseTime.Add(time.Duration(i)*time.Minute).Unix(), token.Value)
  394. require.Nil(t, err)
  395. }
  396. _, err = a.AuthenticateToken(tokens[0])
  397. require.Equal(t, ErrUnauthenticated, err)
  398. _, err = a.AuthenticateToken(tokens[1])
  399. require.Equal(t, ErrUnauthenticated, err)
  400. for i := 2; i < 12; i++ {
  401. userWithToken, err := a.AuthenticateToken(tokens[i])
  402. require.Nil(t, err, "token[%d]=%s failed", i, tokens[i])
  403. require.Equal(t, "ben", userWithToken.Name)
  404. require.Equal(t, tokens[i], userWithToken.Token)
  405. }
  406. var count int
  407. rows, err := a.db.Query(`SELECT COUNT(*) FROM user_token`)
  408. require.Nil(t, err)
  409. require.True(t, rows.Next())
  410. require.Nil(t, rows.Scan(&count))
  411. require.Equal(t, 10, count)
  412. }
  413. func TestManager_EnqueueStats(t *testing.T) {
  414. a, err := newManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, 1500*time.Millisecond)
  415. require.Nil(t, err)
  416. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  417. // Baseline: No messages or emails
  418. u, err := a.User("ben")
  419. require.Nil(t, err)
  420. require.Equal(t, int64(0), u.Stats.Messages)
  421. require.Equal(t, int64(0), u.Stats.Emails)
  422. u.Stats.Messages = 11
  423. u.Stats.Emails = 2
  424. a.EnqueueStats(u)
  425. // Still no change, because it's queued asynchronously
  426. u, err = a.User("ben")
  427. require.Nil(t, err)
  428. require.Equal(t, int64(0), u.Stats.Messages)
  429. require.Equal(t, int64(0), u.Stats.Emails)
  430. // After 2 seconds they should be persisted
  431. time.Sleep(2 * time.Second)
  432. u, err = a.User("ben")
  433. require.Nil(t, err)
  434. require.Equal(t, int64(11), u.Stats.Messages)
  435. require.Equal(t, int64(2), u.Stats.Emails)
  436. }
  437. func TestManager_ChangeSettings(t *testing.T) {
  438. a, err := newManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, 1500*time.Millisecond)
  439. require.Nil(t, err)
  440. require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
  441. // No settings
  442. u, err := a.User("ben")
  443. require.Nil(t, err)
  444. require.Nil(t, u.Prefs.Subscriptions)
  445. require.Nil(t, u.Prefs.Notification)
  446. require.Equal(t, "", u.Prefs.Language)
  447. // Save with new settings
  448. u.Prefs = &Prefs{
  449. Language: "de",
  450. Notification: &NotificationPrefs{
  451. Sound: "ding",
  452. MinPriority: 2,
  453. },
  454. Subscriptions: []*Subscription{
  455. {
  456. ID: "someID",
  457. BaseURL: "https://ntfy.sh",
  458. Topic: "mytopic",
  459. DisplayName: "My Topic",
  460. },
  461. },
  462. }
  463. require.Nil(t, a.ChangeSettings(u))
  464. // Read again
  465. u, err = a.User("ben")
  466. require.Nil(t, err)
  467. require.Equal(t, "de", u.Prefs.Language)
  468. require.Equal(t, "ding", u.Prefs.Notification.Sound)
  469. require.Equal(t, 2, u.Prefs.Notification.MinPriority)
  470. require.Equal(t, 0, u.Prefs.Notification.DeleteAfter)
  471. require.Equal(t, "someID", u.Prefs.Subscriptions[0].ID)
  472. require.Equal(t, "https://ntfy.sh", u.Prefs.Subscriptions[0].BaseURL)
  473. require.Equal(t, "mytopic", u.Prefs.Subscriptions[0].Topic)
  474. require.Equal(t, "My Topic", u.Prefs.Subscriptions[0].DisplayName)
  475. }
  476. func TestSqliteCache_Migration_From1(t *testing.T) {
  477. filename := filepath.Join(t.TempDir(), "user.db")
  478. db, err := sql.Open("sqlite3", filename)
  479. require.Nil(t, err)
  480. // Create "version 1" schema
  481. _, err = db.Exec(`
  482. BEGIN;
  483. CREATE TABLE IF NOT EXISTS user (
  484. user TEXT NOT NULL PRIMARY KEY,
  485. pass TEXT NOT NULL,
  486. role TEXT NOT NULL
  487. );
  488. CREATE TABLE IF NOT EXISTS access (
  489. user TEXT NOT NULL,
  490. topic TEXT NOT NULL,
  491. read INT NOT NULL,
  492. write INT NOT NULL,
  493. PRIMARY KEY (topic, user)
  494. );
  495. CREATE TABLE IF NOT EXISTS schemaVersion (
  496. id INT PRIMARY KEY,
  497. version INT NOT NULL
  498. );
  499. INSERT INTO schemaVersion (id, version) VALUES (1, 1);
  500. COMMIT;
  501. `)
  502. require.Nil(t, err)
  503. // Insert a bunch of users and ACL entries
  504. _, err = db.Exec(`
  505. BEGIN;
  506. INSERT INTO user (user, pass, role) VALUES ('ben', '$2a$10$EEp6gBheOsqEFsXlo523E.gBVoeg1ytphXiEvTPlNzkenBlHZBPQy', 'user');
  507. INSERT INTO user (user, pass, role) VALUES ('phil', '$2a$10$YLiO8U21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C', 'admin');
  508. INSERT INTO access (user, topic, read, write) VALUES ('ben', 'stats', 1, 1);
  509. INSERT INTO access (user, topic, read, write) VALUES ('ben', 'secret', 1, 0);
  510. INSERT INTO access (user, topic, read, write) VALUES ('*', 'stats', 1, 0);
  511. COMMIT;
  512. `)
  513. require.Nil(t, err)
  514. // Create manager to trigger migration
  515. a := newTestManagerFromFile(t, filename, "", PermissionDenyAll, userStatsQueueWriterInterval)
  516. checkSchemaVersion(t, a.db)
  517. users, err := a.Users()
  518. require.Nil(t, err)
  519. require.Equal(t, 3, len(users))
  520. phil, ben, everyone := users[0], users[1], users[2]
  521. philGrants, err := a.Grants("phil")
  522. require.Nil(t, err)
  523. benGrants, err := a.Grants("ben")
  524. require.Nil(t, err)
  525. everyoneGrants, err := a.Grants(Everyone)
  526. require.Nil(t, err)
  527. require.Equal(t, "phil", phil.Name)
  528. require.Equal(t, RoleAdmin, phil.Role)
  529. require.Equal(t, syncTopicLength, len(phil.SyncTopic))
  530. require.Equal(t, 0, len(philGrants))
  531. require.Equal(t, "ben", ben.Name)
  532. require.Equal(t, RoleUser, ben.Role)
  533. require.Equal(t, syncTopicLength, len(ben.SyncTopic))
  534. require.NotEqual(t, ben.SyncTopic, phil.SyncTopic)
  535. require.Equal(t, 2, len(benGrants))
  536. require.Equal(t, "stats", benGrants[0].TopicPattern)
  537. require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
  538. require.Equal(t, "secret", benGrants[1].TopicPattern)
  539. require.Equal(t, PermissionRead, benGrants[1].Allow)
  540. require.Equal(t, Everyone, everyone.Name)
  541. require.Equal(t, RoleAnonymous, everyone.Role)
  542. require.Equal(t, 1, len(everyoneGrants))
  543. require.Equal(t, "stats", everyoneGrants[0].TopicPattern)
  544. require.Equal(t, PermissionRead, everyoneGrants[0].Allow)
  545. }
  546. func checkSchemaVersion(t *testing.T, db *sql.DB) {
  547. rows, err := db.Query(`SELECT version FROM schemaVersion`)
  548. require.Nil(t, err)
  549. require.True(t, rows.Next())
  550. var schemaVersion int
  551. require.Nil(t, rows.Scan(&schemaVersion))
  552. require.Equal(t, currentSchemaVersion, schemaVersion)
  553. require.Nil(t, rows.Close())
  554. }
  555. func newTestManager(t *testing.T, defaultAccess Permission) *Manager {
  556. return newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", defaultAccess, userStatsQueueWriterInterval)
  557. }
  558. func newTestManagerFromFile(t *testing.T, filename, startupQueries string, defaultAccess Permission, statsWriterInterval time.Duration) *Manager {
  559. a, err := newManager(filename, startupQueries, defaultAccess, statsWriterInterval)
  560. require.Nil(t, err)
  561. return a
  562. }