1
0

manager_test.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  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_AddUser_And_Query(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. require.Nil(t, a.ChangeBilling("user", &Billing{
  99. StripeCustomerID: "acct_123",
  100. StripeSubscriptionID: "sub_123",
  101. StripeSubscriptionStatus: "active",
  102. StripeSubscriptionPaidUntil: time.Now().Add(time.Hour),
  103. StripeSubscriptionCancelAt: time.Unix(0, 0),
  104. }))
  105. u, err := a.User("user")
  106. require.Nil(t, err)
  107. require.Equal(t, "user", u.Name)
  108. u2, err := a.UserByID(u.ID)
  109. require.Nil(t, err)
  110. require.Equal(t, u.Name, u2.Name)
  111. u3, err := a.UserByStripeCustomer("acct_123")
  112. require.Nil(t, err)
  113. require.Equal(t, u.ID, u3.ID)
  114. }
  115. func TestManager_Authenticate_Timing(t *testing.T) {
  116. a := newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
  117. require.Nil(t, a.AddUser("user", "pass", RoleAdmin))
  118. // Timing a correct attempt
  119. start := time.Now().UnixMilli()
  120. _, err := a.Authenticate("user", "pass")
  121. require.Nil(t, err)
  122. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  123. // Timing an incorrect attempt
  124. start = time.Now().UnixMilli()
  125. _, err = a.Authenticate("user", "INCORRECT")
  126. require.Equal(t, ErrUnauthenticated, err)
  127. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  128. // Timing a non-existing user attempt
  129. start = time.Now().UnixMilli()
  130. _, err = a.Authenticate("DOES-NOT-EXIST", "hithere")
  131. require.Equal(t, ErrUnauthenticated, err)
  132. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  133. }
  134. func TestManager_MarkUserRemoved_RemoveDeletedUsers(t *testing.T) {
  135. a := newTestManager(t, PermissionDenyAll)
  136. // Create user, add reservations and token
  137. require.Nil(t, a.AddUser("user", "pass", RoleAdmin))
  138. require.Nil(t, a.AddReservation("user", "mytopic", PermissionRead))
  139. u, err := a.User("user")
  140. require.Nil(t, err)
  141. require.False(t, u.Deleted)
  142. token, err := a.CreateToken(u.ID, "", time.Now().Add(time.Hour), netip.IPv4Unspecified())
  143. require.Nil(t, err)
  144. u, err = a.Authenticate("user", "pass")
  145. require.Nil(t, err)
  146. _, err = a.AuthenticateToken(token.Value)
  147. require.Nil(t, err)
  148. reservations, err := a.Reservations("user")
  149. require.Nil(t, err)
  150. require.Equal(t, 1, len(reservations))
  151. // Mark deleted: cannot auth anymore, and all reservations are gone
  152. require.Nil(t, a.MarkUserRemoved(u))
  153. _, err = a.Authenticate("user", "pass")
  154. require.Equal(t, ErrUnauthenticated, err)
  155. _, err = a.AuthenticateToken(token.Value)
  156. require.Equal(t, ErrUnauthenticated, err)
  157. reservations, err = a.Reservations("user")
  158. require.Nil(t, err)
  159. require.Equal(t, 0, len(reservations))
  160. // Make sure user is still there
  161. u, err = a.User("user")
  162. require.Nil(t, err)
  163. require.True(t, u.Deleted)
  164. _, err = a.db.Exec("UPDATE user SET deleted = ? WHERE id = ?", time.Now().Add(-1*(userHardDeleteAfterDuration+time.Hour)).Unix(), u.ID)
  165. require.Nil(t, err)
  166. require.Nil(t, a.RemoveDeletedUsers())
  167. _, err = a.User("user")
  168. require.Equal(t, ErrUserNotFound, err)
  169. }
  170. func TestManager_UserManagement(t *testing.T) {
  171. a := newTestManager(t, PermissionDenyAll)
  172. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin))
  173. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  174. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  175. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  176. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  177. require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
  178. require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
  179. require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
  180. // Query user details
  181. phil, err := a.User("phil")
  182. require.Nil(t, err)
  183. require.Equal(t, "phil", phil.Name)
  184. require.True(t, strings.HasPrefix(phil.Hash, "$2a$04$")) // Min cost for testing
  185. require.Equal(t, RoleAdmin, phil.Role)
  186. philGrants, err := a.Grants("phil")
  187. require.Nil(t, err)
  188. require.Equal(t, []Grant{}, philGrants)
  189. ben, err := a.User("ben")
  190. require.Nil(t, err)
  191. require.Equal(t, "ben", ben.Name)
  192. require.True(t, strings.HasPrefix(ben.Hash, "$2a$04$")) // Min cost for testing
  193. require.Equal(t, RoleUser, ben.Role)
  194. benGrants, err := a.Grants("ben")
  195. require.Nil(t, err)
  196. require.Equal(t, []Grant{
  197. {"mytopic", PermissionReadWrite},
  198. {"writeme", PermissionWrite},
  199. {"readme", PermissionRead},
  200. {"everyonewrite", PermissionDenyAll},
  201. }, benGrants)
  202. everyone, err := a.User(Everyone)
  203. require.Nil(t, err)
  204. require.Equal(t, "*", everyone.Name)
  205. require.Equal(t, "", everyone.Hash)
  206. require.Equal(t, RoleAnonymous, everyone.Role)
  207. everyoneGrants, err := a.Grants(Everyone)
  208. require.Nil(t, err)
  209. require.Equal(t, []Grant{
  210. {"everyonewrite", PermissionReadWrite},
  211. {"announcements", PermissionRead},
  212. }, everyoneGrants)
  213. // Ben: Before revoking
  214. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite)) // Overwrite!
  215. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  216. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  217. require.Nil(t, a.Authorize(ben, "mytopic", PermissionRead))
  218. require.Nil(t, a.Authorize(ben, "mytopic", PermissionWrite))
  219. require.Nil(t, a.Authorize(ben, "readme", PermissionRead))
  220. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
  221. // Revoke access for "ben" to "mytopic", then check again
  222. require.Nil(t, a.ResetAccess("ben", "mytopic"))
  223. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionWrite)) // Revoked
  224. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionRead)) // Revoked
  225. require.Nil(t, a.Authorize(ben, "readme", PermissionRead)) // Unchanged
  226. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite)) // Unchanged
  227. // Revoke rest of the access
  228. require.Nil(t, a.ResetAccess("ben", ""))
  229. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "readme", PermissionRead)) // Revoked
  230. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "wrtiteme", PermissionWrite)) // Revoked
  231. // User list
  232. users, err := a.Users()
  233. require.Nil(t, err)
  234. require.Equal(t, 3, len(users))
  235. require.Equal(t, "phil", users[0].Name)
  236. require.Equal(t, "ben", users[1].Name)
  237. require.Equal(t, "*", users[2].Name)
  238. // Remove user
  239. require.Nil(t, a.RemoveUser("ben"))
  240. _, err = a.User("ben")
  241. require.Equal(t, ErrUserNotFound, err)
  242. users, err = a.Users()
  243. require.Nil(t, err)
  244. require.Equal(t, 2, len(users))
  245. require.Equal(t, "phil", users[0].Name)
  246. require.Equal(t, "*", users[1].Name)
  247. }
  248. func TestManager_ChangePassword(t *testing.T) {
  249. a := newTestManager(t, PermissionDenyAll)
  250. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin))
  251. _, err := a.Authenticate("phil", "phil")
  252. require.Nil(t, err)
  253. require.Nil(t, a.ChangePassword("phil", "newpass"))
  254. _, err = a.Authenticate("phil", "phil")
  255. require.Equal(t, ErrUnauthenticated, err)
  256. _, err = a.Authenticate("phil", "newpass")
  257. require.Nil(t, err)
  258. }
  259. func TestManager_ChangeRole(t *testing.T) {
  260. a := newTestManager(t, PermissionDenyAll)
  261. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  262. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  263. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  264. ben, err := a.User("ben")
  265. require.Nil(t, err)
  266. require.Equal(t, RoleUser, ben.Role)
  267. benGrants, err := a.Grants("ben")
  268. require.Nil(t, err)
  269. require.Equal(t, 2, len(benGrants))
  270. require.Nil(t, a.ChangeRole("ben", RoleAdmin))
  271. ben, err = a.User("ben")
  272. require.Nil(t, err)
  273. require.Equal(t, RoleAdmin, ben.Role)
  274. benGrants, err = a.Grants("ben")
  275. require.Nil(t, err)
  276. require.Equal(t, 0, len(benGrants))
  277. }
  278. func TestManager_Reservations(t *testing.T) {
  279. a := newTestManager(t, PermissionDenyAll)
  280. require.Nil(t, a.AddUser("phil", "phil", RoleUser))
  281. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  282. require.Nil(t, a.AddReservation("ben", "ztopic", PermissionDenyAll))
  283. require.Nil(t, a.AddReservation("ben", "readme", PermissionRead))
  284. require.Nil(t, a.AllowAccess("ben", "something-else", PermissionRead))
  285. reservations, err := a.Reservations("ben")
  286. require.Nil(t, err)
  287. require.Equal(t, 2, len(reservations))
  288. require.Equal(t, Reservation{
  289. Topic: "readme",
  290. Owner: PermissionReadWrite,
  291. Everyone: PermissionRead,
  292. }, reservations[0])
  293. require.Equal(t, Reservation{
  294. Topic: "ztopic",
  295. Owner: PermissionReadWrite,
  296. Everyone: PermissionDenyAll,
  297. }, reservations[1])
  298. b, err := a.HasReservation("ben", "readme")
  299. require.Nil(t, err)
  300. require.True(t, b)
  301. b, err = a.HasReservation("notben", "readme")
  302. require.Nil(t, err)
  303. require.False(t, b)
  304. b, err = a.HasReservation("ben", "something-else")
  305. require.Nil(t, err)
  306. require.False(t, b)
  307. count, err := a.ReservationsCount("ben")
  308. require.Nil(t, err)
  309. require.Equal(t, int64(2), count)
  310. count, err = a.ReservationsCount("phil")
  311. require.Nil(t, err)
  312. require.Equal(t, int64(0), count)
  313. err = a.AllowReservation("phil", "readme")
  314. require.Equal(t, errTopicOwnedByOthers, err)
  315. err = a.AllowReservation("phil", "not-reserved")
  316. require.Nil(t, err)
  317. }
  318. func TestManager_ChangeRoleFromTierUserToAdmin(t *testing.T) {
  319. a := newTestManager(t, PermissionDenyAll)
  320. require.Nil(t, a.AddTier(&Tier{
  321. Code: "pro",
  322. Name: "ntfy Pro",
  323. StripePriceID: "price123",
  324. MessageLimit: 5_000,
  325. MessageExpiryDuration: 3 * 24 * time.Hour,
  326. EmailLimit: 50,
  327. ReservationLimit: 5,
  328. AttachmentFileSizeLimit: 52428800,
  329. AttachmentTotalSizeLimit: 524288000,
  330. AttachmentExpiryDuration: 24 * time.Hour,
  331. }))
  332. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  333. require.Nil(t, a.ChangeTier("ben", "pro"))
  334. require.Nil(t, a.AddReservation("ben", "mytopic", PermissionDenyAll))
  335. ben, err := a.User("ben")
  336. require.Nil(t, err)
  337. require.Equal(t, RoleUser, ben.Role)
  338. require.Equal(t, "pro", ben.Tier.Code)
  339. require.Equal(t, int64(5000), ben.Tier.MessageLimit)
  340. require.Equal(t, 3*24*time.Hour, ben.Tier.MessageExpiryDuration)
  341. require.Equal(t, int64(50), ben.Tier.EmailLimit)
  342. require.Equal(t, int64(5), ben.Tier.ReservationLimit)
  343. require.Equal(t, int64(52428800), ben.Tier.AttachmentFileSizeLimit)
  344. require.Equal(t, int64(524288000), ben.Tier.AttachmentTotalSizeLimit)
  345. require.Equal(t, 24*time.Hour, ben.Tier.AttachmentExpiryDuration)
  346. benGrants, err := a.Grants("ben")
  347. require.Nil(t, err)
  348. require.Equal(t, 1, len(benGrants))
  349. require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
  350. everyoneGrants, err := a.Grants(Everyone)
  351. require.Nil(t, err)
  352. require.Equal(t, 1, len(everyoneGrants))
  353. require.Equal(t, PermissionDenyAll, everyoneGrants[0].Allow)
  354. benReservations, err := a.Reservations("ben")
  355. require.Nil(t, err)
  356. require.Equal(t, 1, len(benReservations))
  357. require.Equal(t, "mytopic", benReservations[0].Topic)
  358. require.Equal(t, PermissionReadWrite, benReservations[0].Owner)
  359. require.Equal(t, PermissionDenyAll, benReservations[0].Everyone)
  360. // Switch to admin, this should remove all grants and owned ACL entries
  361. require.Nil(t, a.ChangeRole("ben", RoleAdmin))
  362. benGrants, err = a.Grants("ben")
  363. require.Nil(t, err)
  364. require.Equal(t, 0, len(benGrants))
  365. everyoneGrants, err = a.Grants(Everyone)
  366. require.Nil(t, err)
  367. require.Equal(t, 0, len(everyoneGrants))
  368. }
  369. func TestManager_Token_Valid(t *testing.T) {
  370. a := newTestManager(t, PermissionDenyAll)
  371. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  372. u, err := a.User("ben")
  373. require.Nil(t, err)
  374. // Create token for user
  375. token, err := a.CreateToken(u.ID, "some label", time.Now().Add(72*time.Hour), netip.IPv4Unspecified())
  376. require.Nil(t, err)
  377. require.NotEmpty(t, token.Value)
  378. require.Equal(t, "some label", token.Label)
  379. require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires.Unix())
  380. u2, err := a.AuthenticateToken(token.Value)
  381. require.Nil(t, err)
  382. require.Equal(t, u.Name, u2.Name)
  383. require.Equal(t, token.Value, u2.Token)
  384. token2, err := a.Token(u.ID, token.Value)
  385. require.Nil(t, err)
  386. require.Equal(t, token.Value, token2.Value)
  387. require.Equal(t, "some label", token2.Label)
  388. tokens, err := a.Tokens(u.ID)
  389. require.Nil(t, err)
  390. require.Equal(t, 1, len(tokens))
  391. require.Equal(t, "some label", tokens[0].Label)
  392. tokens, err = a.Tokens("u_notauser")
  393. require.Nil(t, err)
  394. require.Equal(t, 0, len(tokens))
  395. // Remove token and auth again
  396. require.Nil(t, a.RemoveToken(u2.ID, u2.Token))
  397. u3, err := a.AuthenticateToken(token.Value)
  398. require.Equal(t, ErrUnauthenticated, err)
  399. require.Nil(t, u3)
  400. tokens, err = a.Tokens(u.ID)
  401. require.Nil(t, err)
  402. require.Equal(t, 0, len(tokens))
  403. }
  404. func TestManager_Token_Invalid(t *testing.T) {
  405. a := newTestManager(t, PermissionDenyAll)
  406. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  407. u, err := a.AuthenticateToken(strings.Repeat("x", 32)) // 32 == token length
  408. require.Nil(t, u)
  409. require.Equal(t, ErrUnauthenticated, err)
  410. u, err = a.AuthenticateToken("not long enough anyway")
  411. require.Nil(t, u)
  412. require.Equal(t, ErrUnauthenticated, err)
  413. }
  414. func TestManager_Token_NotFound(t *testing.T) {
  415. a := newTestManager(t, PermissionDenyAll)
  416. _, err := a.Token("u_bla", "notfound")
  417. require.Equal(t, ErrTokenNotFound, err)
  418. }
  419. func TestManager_Token_Expire(t *testing.T) {
  420. a := newTestManager(t, PermissionDenyAll)
  421. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  422. u, err := a.User("ben")
  423. require.Nil(t, err)
  424. // Create tokens for user
  425. token1, err := a.CreateToken(u.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified())
  426. require.Nil(t, err)
  427. require.NotEmpty(t, token1.Value)
  428. require.True(t, time.Now().Add(71*time.Hour).Unix() < token1.Expires.Unix())
  429. token2, err := a.CreateToken(u.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified())
  430. require.Nil(t, err)
  431. require.NotEmpty(t, token2.Value)
  432. require.NotEqual(t, token1.Value, token2.Value)
  433. require.True(t, time.Now().Add(71*time.Hour).Unix() < token2.Expires.Unix())
  434. // See that tokens work
  435. _, err = a.AuthenticateToken(token1.Value)
  436. require.Nil(t, err)
  437. _, err = a.AuthenticateToken(token2.Value)
  438. require.Nil(t, err)
  439. // Modify token expiration in database
  440. _, err = a.db.Exec("UPDATE user_token SET expires = 1 WHERE token = ?", token1.Value)
  441. require.Nil(t, err)
  442. // Now token1 shouldn't work anymore
  443. _, err = a.AuthenticateToken(token1.Value)
  444. require.Equal(t, ErrUnauthenticated, err)
  445. result, err := a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
  446. require.Nil(t, err)
  447. require.True(t, result.Next()) // Still a matching row
  448. require.Nil(t, result.Close())
  449. // Expire tokens and check database rows
  450. require.Nil(t, a.RemoveExpiredTokens())
  451. result, err = a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
  452. require.Nil(t, err)
  453. require.False(t, result.Next()) // No matching row!
  454. require.Nil(t, result.Close())
  455. }
  456. func TestManager_Token_Extend(t *testing.T) {
  457. a := newTestManager(t, PermissionDenyAll)
  458. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  459. // Try to extend token for user without token
  460. u, err := a.User("ben")
  461. require.Nil(t, err)
  462. _, err = a.ChangeToken(u.ID, u.Token, util.String("some label"), util.Time(time.Now().Add(time.Hour)))
  463. require.Equal(t, errNoTokenProvided, err)
  464. // Create token for user
  465. token, err := a.CreateToken(u.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified())
  466. require.Nil(t, err)
  467. require.NotEmpty(t, token.Value)
  468. userWithToken, err := a.AuthenticateToken(token.Value)
  469. require.Nil(t, err)
  470. extendedToken, err := a.ChangeToken(userWithToken.ID, userWithToken.Token, util.String("changed label"), util.Time(time.Now().Add(100*time.Hour)))
  471. require.Nil(t, err)
  472. require.Equal(t, token.Value, extendedToken.Value)
  473. require.Equal(t, "changed label", extendedToken.Label)
  474. require.True(t, token.Expires.Unix() < extendedToken.Expires.Unix())
  475. require.True(t, time.Now().Add(99*time.Hour).Unix() < extendedToken.Expires.Unix())
  476. }
  477. func TestManager_Token_MaxCount_AutoDelete(t *testing.T) {
  478. a := newTestManager(t, PermissionDenyAll)
  479. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  480. // Try to extend token for user without token
  481. u, err := a.User("ben")
  482. require.Nil(t, err)
  483. // Tokens
  484. baseTime := time.Now().Add(24 * time.Hour)
  485. tokens := make([]string, 0)
  486. for i := 0; i < 22; i++ {
  487. token, err := a.CreateToken(u.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified())
  488. require.Nil(t, err)
  489. require.NotEmpty(t, token.Value)
  490. tokens = append(tokens, token.Value)
  491. // Manually modify expiry date to avoid sorting issues (this is a hack)
  492. _, err = a.db.Exec(`UPDATE user_token SET expires=? WHERE token=?`, baseTime.Add(time.Duration(i)*time.Minute).Unix(), token.Value)
  493. require.Nil(t, err)
  494. }
  495. _, err = a.AuthenticateToken(tokens[0])
  496. require.Equal(t, ErrUnauthenticated, err)
  497. _, err = a.AuthenticateToken(tokens[1])
  498. require.Equal(t, ErrUnauthenticated, err)
  499. for i := 2; i < 22; i++ {
  500. userWithToken, err := a.AuthenticateToken(tokens[i])
  501. require.Nil(t, err, "token[%d]=%s failed", i, tokens[i])
  502. require.Equal(t, "ben", userWithToken.Name)
  503. require.Equal(t, tokens[i], userWithToken.Token)
  504. }
  505. var count int
  506. rows, err := a.db.Query(`SELECT COUNT(*) FROM user_token`)
  507. require.Nil(t, err)
  508. require.True(t, rows.Next())
  509. require.Nil(t, rows.Scan(&count))
  510. require.Equal(t, 20, count)
  511. }
  512. func TestManager_EnqueueStats_ResetStats(t *testing.T) {
  513. a, err := NewManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, bcrypt.MinCost, 1500*time.Millisecond)
  514. require.Nil(t, err)
  515. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  516. // Baseline: No messages or emails
  517. u, err := a.User("ben")
  518. require.Nil(t, err)
  519. require.Equal(t, int64(0), u.Stats.Messages)
  520. require.Equal(t, int64(0), u.Stats.Emails)
  521. a.EnqueueUserStats(u.ID, &Stats{
  522. Messages: 11,
  523. Emails: 2,
  524. })
  525. // Still no change, because it's queued asynchronously
  526. u, err = a.User("ben")
  527. require.Nil(t, err)
  528. require.Equal(t, int64(0), u.Stats.Messages)
  529. require.Equal(t, int64(0), u.Stats.Emails)
  530. // After 2 seconds they should be persisted
  531. time.Sleep(2 * time.Second)
  532. u, err = a.User("ben")
  533. require.Nil(t, err)
  534. require.Equal(t, int64(11), u.Stats.Messages)
  535. require.Equal(t, int64(2), u.Stats.Emails)
  536. // Now reset stats (enqueued stats will be thrown out)
  537. a.EnqueueUserStats(u.ID, &Stats{
  538. Messages: 99,
  539. Emails: 23,
  540. })
  541. require.Nil(t, a.ResetStats())
  542. u, err = a.User("ben")
  543. require.Nil(t, err)
  544. require.Equal(t, int64(0), u.Stats.Messages)
  545. require.Equal(t, int64(0), u.Stats.Emails)
  546. }
  547. func TestManager_EnqueueTokenUpdate(t *testing.T) {
  548. a, err := NewManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, bcrypt.MinCost, 500*time.Millisecond)
  549. require.Nil(t, err)
  550. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  551. // Create user and token
  552. u, err := a.User("ben")
  553. require.Nil(t, err)
  554. token, err := a.CreateToken(u.ID, "", time.Now().Add(time.Hour), netip.IPv4Unspecified())
  555. require.Nil(t, err)
  556. // Queue token update
  557. a.EnqueueTokenUpdate(token.Value, &TokenUpdate{
  558. LastAccess: time.Unix(111, 0).UTC(),
  559. LastOrigin: netip.MustParseAddr("1.2.3.3"),
  560. })
  561. // Token has not changed yet.
  562. token2, err := a.Token(u.ID, token.Value)
  563. require.Nil(t, err)
  564. require.Equal(t, token.LastAccess.Unix(), token2.LastAccess.Unix())
  565. require.Equal(t, token.LastOrigin, token2.LastOrigin)
  566. // After a second or so they should be persisted
  567. time.Sleep(time.Second)
  568. token3, err := a.Token(u.ID, token.Value)
  569. require.Nil(t, err)
  570. require.Equal(t, time.Unix(111, 0).UTC().Unix(), token3.LastAccess.Unix())
  571. require.Equal(t, netip.MustParseAddr("1.2.3.3"), token3.LastOrigin)
  572. }
  573. func TestManager_ChangeSettings(t *testing.T) {
  574. a, err := NewManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, bcrypt.MinCost, 1500*time.Millisecond)
  575. require.Nil(t, err)
  576. require.Nil(t, a.AddUser("ben", "ben", RoleUser))
  577. // No settings
  578. u, err := a.User("ben")
  579. require.Nil(t, err)
  580. require.Nil(t, u.Prefs.Subscriptions)
  581. require.Nil(t, u.Prefs.Notification)
  582. require.Nil(t, u.Prefs.Language)
  583. // Save with new settings
  584. prefs := &Prefs{
  585. Language: util.String("de"),
  586. Notification: &NotificationPrefs{
  587. Sound: util.String("ding"),
  588. MinPriority: util.Int(2),
  589. },
  590. Subscriptions: []*Subscription{
  591. {
  592. ID: "someID",
  593. BaseURL: "https://ntfy.sh",
  594. Topic: "mytopic",
  595. DisplayName: util.String("My Topic"),
  596. },
  597. },
  598. }
  599. require.Nil(t, a.ChangeSettings(u.ID, prefs))
  600. // Read again
  601. u, err = a.User("ben")
  602. require.Nil(t, err)
  603. require.Equal(t, util.String("de"), u.Prefs.Language)
  604. require.Equal(t, util.String("ding"), u.Prefs.Notification.Sound)
  605. require.Equal(t, util.Int(2), u.Prefs.Notification.MinPriority)
  606. require.Nil(t, u.Prefs.Notification.DeleteAfter)
  607. require.Equal(t, "someID", u.Prefs.Subscriptions[0].ID)
  608. require.Equal(t, "https://ntfy.sh", u.Prefs.Subscriptions[0].BaseURL)
  609. require.Equal(t, "mytopic", u.Prefs.Subscriptions[0].Topic)
  610. require.Equal(t, util.String("My Topic"), u.Prefs.Subscriptions[0].DisplayName)
  611. }
  612. func TestManager_Tier_Create(t *testing.T) {
  613. a := newTestManager(t, PermissionDenyAll)
  614. // Create tier and user
  615. require.Nil(t, a.AddTier(&Tier{
  616. Code: "pro",
  617. Name: "Pro",
  618. MessageLimit: 123,
  619. MessageExpiryDuration: 86400 * time.Second,
  620. EmailLimit: 32,
  621. ReservationLimit: 2,
  622. AttachmentFileSizeLimit: 1231231,
  623. AttachmentTotalSizeLimit: 123123,
  624. AttachmentExpiryDuration: 10800 * time.Second,
  625. AttachmentBandwidthLimit: 21474836480,
  626. }))
  627. require.Nil(t, a.AddUser("phil", "phil", RoleUser))
  628. require.Nil(t, a.ChangeTier("phil", "pro"))
  629. ti, err := a.Tier("pro")
  630. require.Nil(t, err)
  631. u, err := a.User("phil")
  632. require.Nil(t, err)
  633. // These are populated by different SQL queries
  634. require.Equal(t, ti, u.Tier)
  635. // Fields
  636. require.True(t, strings.HasPrefix(ti.ID, "ti_"))
  637. require.Equal(t, "pro", ti.Code)
  638. require.Equal(t, "Pro", ti.Name)
  639. require.Equal(t, int64(123), ti.MessageLimit)
  640. require.Equal(t, 86400*time.Second, ti.MessageExpiryDuration)
  641. require.Equal(t, int64(32), ti.EmailLimit)
  642. require.Equal(t, int64(2), ti.ReservationLimit)
  643. require.Equal(t, int64(1231231), ti.AttachmentFileSizeLimit)
  644. require.Equal(t, int64(123123), ti.AttachmentTotalSizeLimit)
  645. require.Equal(t, 10800*time.Second, ti.AttachmentExpiryDuration)
  646. require.Equal(t, int64(21474836480), ti.AttachmentBandwidthLimit)
  647. }
  648. func TestAccount_Tier_Create_With_ID(t *testing.T) {
  649. a := newTestManager(t, PermissionDenyAll)
  650. require.Nil(t, a.AddTier(&Tier{
  651. ID: "ti_123",
  652. Code: "pro",
  653. }))
  654. ti, err := a.Tier("pro")
  655. require.Nil(t, err)
  656. require.Equal(t, "ti_123", ti.ID)
  657. }
  658. func TestSqliteCache_Migration_From1(t *testing.T) {
  659. filename := filepath.Join(t.TempDir(), "user.db")
  660. db, err := sql.Open("sqlite3", filename)
  661. require.Nil(t, err)
  662. // Create "version 1" schema
  663. _, err = db.Exec(`
  664. BEGIN;
  665. CREATE TABLE IF NOT EXISTS user (
  666. user TEXT NOT NULL PRIMARY KEY,
  667. pass TEXT NOT NULL,
  668. role TEXT NOT NULL
  669. );
  670. CREATE TABLE IF NOT EXISTS access (
  671. user TEXT NOT NULL,
  672. topic TEXT NOT NULL,
  673. read INT NOT NULL,
  674. write INT NOT NULL,
  675. PRIMARY KEY (topic, user)
  676. );
  677. CREATE TABLE IF NOT EXISTS schemaVersion (
  678. id INT PRIMARY KEY,
  679. version INT NOT NULL
  680. );
  681. INSERT INTO schemaVersion (id, version) VALUES (1, 1);
  682. COMMIT;
  683. `)
  684. require.Nil(t, err)
  685. // Insert a bunch of users and ACL entries
  686. _, err = db.Exec(`
  687. BEGIN;
  688. INSERT INTO user (user, pass, role) VALUES ('ben', '$2a$10$EEp6gBheOsqEFsXlo523E.gBVoeg1ytphXiEvTPlNzkenBlHZBPQy', 'user');
  689. INSERT INTO user (user, pass, role) VALUES ('phil', '$2a$10$YLiO8U21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C', 'admin');
  690. INSERT INTO access (user, topic, read, write) VALUES ('ben', 'stats', 1, 1);
  691. INSERT INTO access (user, topic, read, write) VALUES ('ben', 'secret', 1, 0);
  692. INSERT INTO access (user, topic, read, write) VALUES ('*', 'stats', 1, 0);
  693. COMMIT;
  694. `)
  695. require.Nil(t, err)
  696. // Create manager to trigger migration
  697. a := newTestManagerFromFile(t, filename, "", PermissionDenyAll, bcrypt.MinCost, DefaultUserStatsQueueWriterInterval)
  698. checkSchemaVersion(t, a.db)
  699. users, err := a.Users()
  700. require.Nil(t, err)
  701. require.Equal(t, 3, len(users))
  702. phil, ben, everyone := users[0], users[1], users[2]
  703. philGrants, err := a.Grants("phil")
  704. require.Nil(t, err)
  705. benGrants, err := a.Grants("ben")
  706. require.Nil(t, err)
  707. everyoneGrants, err := a.Grants(Everyone)
  708. require.Nil(t, err)
  709. require.True(t, strings.HasPrefix(phil.ID, "u_"))
  710. require.Equal(t, "phil", phil.Name)
  711. require.Equal(t, RoleAdmin, phil.Role)
  712. require.Equal(t, syncTopicLength, len(phil.SyncTopic))
  713. require.Equal(t, 0, len(philGrants))
  714. require.True(t, strings.HasPrefix(ben.ID, "u_"))
  715. require.NotEqual(t, phil.ID, ben.ID)
  716. require.Equal(t, "ben", ben.Name)
  717. require.Equal(t, RoleUser, ben.Role)
  718. require.Equal(t, syncTopicLength, len(ben.SyncTopic))
  719. require.NotEqual(t, ben.SyncTopic, phil.SyncTopic)
  720. require.Equal(t, 2, len(benGrants))
  721. require.Equal(t, "stats", benGrants[0].TopicPattern)
  722. require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
  723. require.Equal(t, "secret", benGrants[1].TopicPattern)
  724. require.Equal(t, PermissionRead, benGrants[1].Allow)
  725. require.Equal(t, "u_everyone", everyone.ID)
  726. require.Equal(t, Everyone, everyone.Name)
  727. require.Equal(t, RoleAnonymous, everyone.Role)
  728. require.Equal(t, 1, len(everyoneGrants))
  729. require.Equal(t, "stats", everyoneGrants[0].TopicPattern)
  730. require.Equal(t, PermissionRead, everyoneGrants[0].Allow)
  731. }
  732. func checkSchemaVersion(t *testing.T, db *sql.DB) {
  733. rows, err := db.Query(`SELECT version FROM schemaVersion`)
  734. require.Nil(t, err)
  735. require.True(t, rows.Next())
  736. var schemaVersion int
  737. require.Nil(t, rows.Scan(&schemaVersion))
  738. require.Equal(t, currentSchemaVersion, schemaVersion)
  739. require.Nil(t, rows.Close())
  740. }
  741. func newTestManager(t *testing.T, defaultAccess Permission) *Manager {
  742. return newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", defaultAccess, bcrypt.MinCost, DefaultUserStatsQueueWriterInterval)
  743. }
  744. func newTestManagerFromFile(t *testing.T, filename, startupQueries string, defaultAccess Permission, bcryptCost int, statsWriterInterval time.Duration) *Manager {
  745. a, err := NewManager(filename, startupQueries, defaultAccess, bcryptCost, statsWriterInterval)
  746. require.Nil(t, err)
  747. return a
  748. }