manager_test.go 27 KB

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