manager_test.go 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  1. package user
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "github.com/stretchr/testify/require"
  6. "golang.org/x/crypto/bcrypt"
  7. "heckel.io/ntfy/v2/util"
  8. "net/netip"
  9. "path/filepath"
  10. "strings"
  11. "testing"
  12. "time"
  13. )
  14. const minBcryptTimingMillis = int64(40) // Ideally should be >100ms, but this should also run on a Raspberry Pi without massive resources
  15. func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
  16. a := newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
  17. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, false))
  18. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  19. require.Nil(t, a.AddUser("john", "john", RoleUser, false))
  20. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  21. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  22. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  23. require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
  24. require.Nil(t, a.AllowAccess("john", "*", PermissionRead))
  25. require.Nil(t, a.AllowAccess("john", "mytopic*", PermissionReadWrite))
  26. require.Nil(t, a.AllowAccess("john", "mytopic_ro*", PermissionRead))
  27. require.Nil(t, a.AllowAccess("john", "mytopic_deny*", PermissionDenyAll))
  28. require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
  29. require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
  30. require.Nil(t, a.AllowAccess(Everyone, "up*", PermissionWrite)) // Everyone can write to /up*
  31. phil, err := a.Authenticate("phil", "phil")
  32. require.Nil(t, err)
  33. require.Equal(t, "phil", phil.Name)
  34. require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
  35. require.Equal(t, RoleAdmin, phil.Role)
  36. philGrants, err := a.Grants("phil")
  37. require.Nil(t, err)
  38. require.Equal(t, []Grant{}, philGrants)
  39. ben, err := a.Authenticate("ben", "ben")
  40. require.Nil(t, err)
  41. require.Equal(t, "ben", ben.Name)
  42. require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
  43. require.Equal(t, RoleUser, ben.Role)
  44. benGrants, err := a.Grants("ben")
  45. require.Nil(t, err)
  46. require.Equal(t, []Grant{
  47. {"everyonewrite", PermissionDenyAll, false},
  48. {"mytopic", PermissionReadWrite, false},
  49. {"writeme", PermissionWrite, false},
  50. {"readme", PermissionRead, false},
  51. }, benGrants)
  52. john, err := a.Authenticate("john", "john")
  53. require.Nil(t, err)
  54. require.Equal(t, "john", john.Name)
  55. require.True(t, strings.HasPrefix(john.Hash, "$2a$10$"))
  56. require.Equal(t, RoleUser, john.Role)
  57. johnGrants, err := a.Grants("john")
  58. require.Nil(t, err)
  59. require.Equal(t, []Grant{
  60. {"mytopic_deny*", PermissionDenyAll, false},
  61. {"mytopic_ro*", PermissionRead, false},
  62. {"mytopic*", PermissionReadWrite, false},
  63. {"*", PermissionRead, false},
  64. }, johnGrants)
  65. notben, err := a.Authenticate("ben", "this is wrong")
  66. require.Nil(t, notben)
  67. require.Equal(t, ErrUnauthenticated, err)
  68. // Admin can do everything
  69. require.Nil(t, a.Authorize(phil, "sometopic", PermissionWrite))
  70. require.Nil(t, a.Authorize(phil, "mytopic", PermissionRead))
  71. require.Nil(t, a.Authorize(phil, "readme", PermissionWrite))
  72. require.Nil(t, a.Authorize(phil, "writeme", PermissionWrite))
  73. require.Nil(t, a.Authorize(phil, "announcements", PermissionWrite))
  74. require.Nil(t, a.Authorize(phil, "everyonewrite", PermissionWrite))
  75. // User cannot do everything
  76. require.Nil(t, a.Authorize(ben, "mytopic", PermissionWrite))
  77. require.Nil(t, a.Authorize(ben, "mytopic", PermissionRead))
  78. require.Nil(t, a.Authorize(ben, "readme", PermissionRead))
  79. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "readme", PermissionWrite))
  80. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "writeme", PermissionRead))
  81. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
  82. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
  83. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "everyonewrite", PermissionRead))
  84. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "everyonewrite", PermissionWrite))
  85. require.Nil(t, a.Authorize(ben, "announcements", PermissionRead))
  86. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "announcements", PermissionWrite))
  87. // User john should have
  88. // "deny" to mytopic_deny*,
  89. // "ro" to mytopic_ro*,
  90. // "rw" to mytopic*,
  91. // "ro" to the rest
  92. require.Equal(t, ErrUnauthorized, a.Authorize(john, "mytopic_deny_case", PermissionRead))
  93. require.Equal(t, ErrUnauthorized, a.Authorize(john, "mytopic_deny_case", PermissionWrite))
  94. require.Nil(t, a.Authorize(john, "mytopic_ro_test_case", PermissionRead))
  95. require.Equal(t, ErrUnauthorized, a.Authorize(john, "mytopic_ro_test_case", PermissionWrite))
  96. require.Nil(t, a.Authorize(john, "mytopic_case1", PermissionRead))
  97. require.Nil(t, a.Authorize(john, "mytopic_case1", PermissionWrite))
  98. require.Nil(t, a.Authorize(john, "readme", PermissionRead))
  99. require.Equal(t, ErrUnauthorized, a.Authorize(john, "writeme", PermissionWrite))
  100. // Everyone else can do barely anything
  101. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionRead))
  102. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionWrite))
  103. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopic", PermissionRead))
  104. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopic", PermissionWrite))
  105. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "readme", PermissionRead))
  106. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "readme", PermissionWrite))
  107. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "writeme", PermissionRead))
  108. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "writeme", PermissionWrite))
  109. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "announcements", PermissionWrite))
  110. require.Nil(t, a.Authorize(nil, "announcements", PermissionRead))
  111. require.Nil(t, a.Authorize(nil, "everyonewrite", PermissionRead))
  112. require.Nil(t, a.Authorize(nil, "everyonewrite", PermissionWrite))
  113. require.Nil(t, a.Authorize(nil, "up1234", PermissionWrite)) // Wildcard permission
  114. require.Nil(t, a.Authorize(nil, "up5678", PermissionWrite))
  115. }
  116. func TestManager_Access_Order_LengthWriteRead(t *testing.T) {
  117. // This test validates issue #914 / #917, i.e. that write permissions are prioritized over read permissions,
  118. // and longer ACL rules are prioritized as well.
  119. a := newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
  120. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  121. require.Nil(t, a.AllowAccess("ben", "test*", PermissionReadWrite))
  122. require.Nil(t, a.AllowAccess("ben", "*", PermissionRead))
  123. ben, err := a.Authenticate("ben", "ben")
  124. require.Nil(t, err)
  125. require.Nil(t, a.Authorize(ben, "any-topic-can-be-read", PermissionRead))
  126. require.Nil(t, a.Authorize(ben, "this-too", PermissionRead))
  127. require.Nil(t, a.Authorize(ben, "test123", PermissionWrite))
  128. }
  129. func TestManager_AddUser_Invalid(t *testing.T) {
  130. a := newTestManager(t, PermissionDenyAll)
  131. require.Equal(t, ErrInvalidArgument, a.AddUser(" invalid ", "pass", RoleAdmin, false))
  132. require.Equal(t, ErrInvalidArgument, a.AddUser("validuser", "pass", "invalid-role", false))
  133. }
  134. func TestManager_AddUser_Timing(t *testing.T) {
  135. a := newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
  136. start := time.Now().UnixMilli()
  137. require.Nil(t, a.AddUser("user", "pass", RoleAdmin, false))
  138. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
  139. }
  140. func TestManager_AddUser_And_Query(t *testing.T) {
  141. a := newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
  142. require.Nil(t, a.AddUser("user", "pass", RoleAdmin, false))
  143. require.Nil(t, a.ChangeBilling("user", &Billing{
  144. StripeCustomerID: "acct_123",
  145. StripeSubscriptionID: "sub_123",
  146. StripeSubscriptionStatus: "active",
  147. StripeSubscriptionInterval: "month",
  148. StripeSubscriptionPaidUntil: time.Now().Add(time.Hour),
  149. StripeSubscriptionCancelAt: time.Unix(0, 0),
  150. }))
  151. u, err := a.User("user")
  152. require.Nil(t, err)
  153. require.Equal(t, "user", u.Name)
  154. u2, err := a.UserByID(u.ID)
  155. require.Nil(t, err)
  156. require.Equal(t, u.Name, u2.Name)
  157. u3, err := a.UserByStripeCustomer("acct_123")
  158. require.Nil(t, err)
  159. require.Equal(t, u.ID, u3.ID)
  160. }
  161. func TestManager_MarkUserRemoved_RemoveDeletedUsers(t *testing.T) {
  162. a := newTestManager(t, PermissionDenyAll)
  163. // Create user, add reservations and token
  164. require.Nil(t, a.AddUser("user", "pass", RoleAdmin, false))
  165. require.Nil(t, a.AddReservation("user", "mytopic", PermissionRead))
  166. u, err := a.User("user")
  167. require.Nil(t, err)
  168. require.False(t, u.Deleted)
  169. token, err := a.CreateToken(u.ID, "", time.Now().Add(time.Hour), netip.IPv4Unspecified(), false)
  170. require.Nil(t, err)
  171. u, err = a.Authenticate("user", "pass")
  172. require.Nil(t, err)
  173. _, err = a.AuthenticateToken(token.Value)
  174. require.Nil(t, err)
  175. reservations, err := a.Reservations("user")
  176. require.Nil(t, err)
  177. require.Equal(t, 1, len(reservations))
  178. // Mark deleted: cannot auth anymore, and all reservations are gone
  179. require.Nil(t, a.MarkUserRemoved(u))
  180. _, err = a.Authenticate("user", "pass")
  181. require.Equal(t, ErrUnauthenticated, err)
  182. _, err = a.AuthenticateToken(token.Value)
  183. require.Equal(t, ErrUnauthenticated, err)
  184. reservations, err = a.Reservations("user")
  185. require.Nil(t, err)
  186. require.Equal(t, 0, len(reservations))
  187. // Make sure user is still there
  188. u, err = a.User("user")
  189. require.Nil(t, err)
  190. require.True(t, u.Deleted)
  191. _, err = a.db.Exec("UPDATE user SET deleted = ? WHERE id = ?", time.Now().Add(-1*(userHardDeleteAfterDuration+time.Hour)).Unix(), u.ID)
  192. require.Nil(t, err)
  193. require.Nil(t, a.RemoveDeletedUsers())
  194. _, err = a.User("user")
  195. require.Equal(t, ErrUserNotFound, err)
  196. }
  197. func TestManager_CreateToken_Only_Lower(t *testing.T) {
  198. a := newTestManager(t, PermissionDenyAll)
  199. // Create user, add reservations and token
  200. require.Nil(t, a.AddUser("user", "pass", RoleAdmin, false))
  201. u, err := a.User("user")
  202. require.Nil(t, err)
  203. token, err := a.CreateToken(u.ID, "", time.Now().Add(time.Hour), netip.IPv4Unspecified(), false)
  204. require.Nil(t, err)
  205. require.Equal(t, token.Value, strings.ToLower(token.Value))
  206. }
  207. func TestManager_UserManagement(t *testing.T) {
  208. a := newTestManager(t, PermissionDenyAll)
  209. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, false))
  210. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  211. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  212. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  213. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  214. require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
  215. require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
  216. require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
  217. // Query user details
  218. phil, err := a.User("phil")
  219. require.Nil(t, err)
  220. require.Equal(t, "phil", phil.Name)
  221. require.True(t, strings.HasPrefix(phil.Hash, "$2a$04$")) // Min cost for testing
  222. require.Equal(t, RoleAdmin, phil.Role)
  223. philGrants, err := a.Grants("phil")
  224. require.Nil(t, err)
  225. require.Equal(t, []Grant{}, philGrants)
  226. ben, err := a.User("ben")
  227. require.Nil(t, err)
  228. require.Equal(t, "ben", ben.Name)
  229. require.True(t, strings.HasPrefix(ben.Hash, "$2a$04$")) // Min cost for testing
  230. require.Equal(t, RoleUser, ben.Role)
  231. benGrants, err := a.Grants("ben")
  232. require.Nil(t, err)
  233. require.Equal(t, []Grant{
  234. {"everyonewrite", PermissionDenyAll, false},
  235. {"mytopic", PermissionReadWrite, false},
  236. {"writeme", PermissionWrite, false},
  237. {"readme", PermissionRead, false},
  238. }, benGrants)
  239. everyone, err := a.User(Everyone)
  240. require.Nil(t, err)
  241. require.Equal(t, "*", everyone.Name)
  242. require.Equal(t, "", everyone.Hash)
  243. require.Equal(t, RoleAnonymous, everyone.Role)
  244. everyoneGrants, err := a.Grants(Everyone)
  245. require.Nil(t, err)
  246. require.Equal(t, []Grant{
  247. {"everyonewrite", PermissionReadWrite, false},
  248. {"announcements", PermissionRead, false},
  249. }, everyoneGrants)
  250. // Ben: Before revoking
  251. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite)) // Overwrite!
  252. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  253. require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
  254. require.Nil(t, a.Authorize(ben, "mytopic", PermissionRead))
  255. require.Nil(t, a.Authorize(ben, "mytopic", PermissionWrite))
  256. require.Nil(t, a.Authorize(ben, "readme", PermissionRead))
  257. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
  258. // Revoke access for "ben" to "mytopic", then check again
  259. require.Nil(t, a.ResetAccess("ben", "mytopic"))
  260. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionWrite)) // Revoked
  261. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionRead)) // Revoked
  262. require.Nil(t, a.Authorize(ben, "readme", PermissionRead)) // Unchanged
  263. require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite)) // Unchanged
  264. // Revoke rest of the access
  265. require.Nil(t, a.ResetAccess("ben", ""))
  266. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "readme", PermissionRead)) // Revoked
  267. require.Equal(t, ErrUnauthorized, a.Authorize(ben, "wrtiteme", PermissionWrite)) // Revoked
  268. // User list
  269. users, err := a.Users()
  270. require.Nil(t, err)
  271. require.Equal(t, 3, len(users))
  272. require.Equal(t, "phil", users[0].Name)
  273. require.Equal(t, "ben", users[1].Name)
  274. require.Equal(t, "*", users[2].Name)
  275. // Remove user
  276. require.Nil(t, a.RemoveUser("ben"))
  277. _, err = a.User("ben")
  278. require.Equal(t, ErrUserNotFound, err)
  279. users, err = a.Users()
  280. require.Nil(t, err)
  281. require.Equal(t, 2, len(users))
  282. require.Equal(t, "phil", users[0].Name)
  283. require.Equal(t, "*", users[1].Name)
  284. }
  285. func TestManager_ChangePassword(t *testing.T) {
  286. a := newTestManager(t, PermissionDenyAll)
  287. require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, false))
  288. require.Nil(t, a.AddUser("jane", "$2a$10$OyqU72muEy7VMd1SAU2Iru5IbeSMgrtCGHu/fWLmxL1MwlijQXWbG", RoleUser, true))
  289. _, err := a.Authenticate("phil", "phil")
  290. require.Nil(t, err)
  291. _, err = a.Authenticate("jane", "jane")
  292. require.Nil(t, err)
  293. require.Nil(t, a.ChangePassword("phil", "newpass", false))
  294. _, err = a.Authenticate("phil", "phil")
  295. require.Equal(t, ErrUnauthenticated, err)
  296. _, err = a.Authenticate("phil", "newpass")
  297. require.Nil(t, err)
  298. require.Nil(t, a.ChangePassword("jane", "$2a$10$CNaCW.q1R431urlbQ5Drh.zl48TiiOeJSmZgfcswkZiPbJGQ1ApSS", true))
  299. _, err = a.Authenticate("jane", "jane")
  300. require.Equal(t, ErrUnauthenticated, err)
  301. _, err = a.Authenticate("jane", "newpass")
  302. require.Nil(t, err)
  303. }
  304. func TestManager_ChangeRole(t *testing.T) {
  305. a := newTestManager(t, PermissionDenyAll)
  306. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  307. require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
  308. require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
  309. ben, err := a.User("ben")
  310. require.Nil(t, err)
  311. require.Equal(t, RoleUser, ben.Role)
  312. benGrants, err := a.Grants("ben")
  313. require.Nil(t, err)
  314. require.Equal(t, 2, len(benGrants))
  315. require.Nil(t, a.ChangeRole("ben", RoleAdmin))
  316. ben, err = a.User("ben")
  317. require.Nil(t, err)
  318. require.Equal(t, RoleAdmin, ben.Role)
  319. benGrants, err = a.Grants("ben")
  320. require.Nil(t, err)
  321. require.Equal(t, 0, len(benGrants))
  322. }
  323. func TestManager_Reservations(t *testing.T) {
  324. a := newTestManager(t, PermissionDenyAll)
  325. require.Nil(t, a.AddUser("phil", "phil", RoleUser, false))
  326. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  327. require.Nil(t, a.AddReservation("ben", "ztopic_", PermissionDenyAll))
  328. require.Nil(t, a.AddReservation("ben", "readme", PermissionRead))
  329. require.Nil(t, a.AllowAccess("ben", "something-else", PermissionRead))
  330. reservations, err := a.Reservations("ben")
  331. require.Nil(t, err)
  332. require.Equal(t, 2, len(reservations))
  333. require.Equal(t, Reservation{
  334. Topic: "readme",
  335. Owner: PermissionReadWrite,
  336. Everyone: PermissionRead,
  337. }, reservations[0])
  338. require.Equal(t, Reservation{
  339. Topic: "ztopic_",
  340. Owner: PermissionReadWrite,
  341. Everyone: PermissionDenyAll,
  342. }, reservations[1])
  343. b, err := a.HasReservation("ben", "readme")
  344. require.Nil(t, err)
  345. require.True(t, b)
  346. b, err = a.HasReservation("ben", "ztopic_")
  347. require.Nil(t, err)
  348. require.True(t, b)
  349. b, err = a.HasReservation("ben", "ztopicX") // _ != X (used to be a SQL wildcard issue)
  350. require.Nil(t, err)
  351. require.False(t, b)
  352. b, err = a.HasReservation("notben", "readme")
  353. require.Nil(t, err)
  354. require.False(t, b)
  355. b, err = a.HasReservation("ben", "something-else")
  356. require.Nil(t, err)
  357. require.False(t, b)
  358. count, err := a.ReservationsCount("ben")
  359. require.Nil(t, err)
  360. require.Equal(t, int64(2), count)
  361. count, err = a.ReservationsCount("phil")
  362. require.Nil(t, err)
  363. require.Equal(t, int64(0), count)
  364. err = a.AllowReservation("phil", "readme")
  365. require.Equal(t, errTopicOwnedByOthers, err)
  366. err = a.AllowReservation("phil", "ztopic_")
  367. require.Equal(t, errTopicOwnedByOthers, err)
  368. err = a.AllowReservation("phil", "ztopicX")
  369. require.Nil(t, err)
  370. err = a.AllowReservation("phil", "not-reserved")
  371. require.Nil(t, err)
  372. // Now remove them again
  373. require.Nil(t, a.RemoveReservations("ben", "ztopic_", "readme"))
  374. count, err = a.ReservationsCount("ben")
  375. require.Nil(t, err)
  376. require.Equal(t, int64(0), count)
  377. }
  378. func TestManager_ChangeRoleFromTierUserToAdmin(t *testing.T) {
  379. a := newTestManager(t, PermissionDenyAll)
  380. require.Nil(t, a.AddTier(&Tier{
  381. Code: "pro",
  382. Name: "ntfy Pro",
  383. StripeMonthlyPriceID: "price123",
  384. MessageLimit: 5_000,
  385. MessageExpiryDuration: 3 * 24 * time.Hour,
  386. EmailLimit: 50,
  387. ReservationLimit: 5,
  388. AttachmentFileSizeLimit: 52428800,
  389. AttachmentTotalSizeLimit: 524288000,
  390. AttachmentExpiryDuration: 24 * time.Hour,
  391. }))
  392. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  393. require.Nil(t, a.ChangeTier("ben", "pro"))
  394. require.Nil(t, a.AddReservation("ben", "mytopic", PermissionDenyAll))
  395. ben, err := a.User("ben")
  396. require.Nil(t, err)
  397. require.Equal(t, RoleUser, ben.Role)
  398. require.Equal(t, "pro", ben.Tier.Code)
  399. require.Equal(t, int64(5000), ben.Tier.MessageLimit)
  400. require.Equal(t, 3*24*time.Hour, ben.Tier.MessageExpiryDuration)
  401. require.Equal(t, int64(50), ben.Tier.EmailLimit)
  402. require.Equal(t, int64(5), ben.Tier.ReservationLimit)
  403. require.Equal(t, int64(52428800), ben.Tier.AttachmentFileSizeLimit)
  404. require.Equal(t, int64(524288000), ben.Tier.AttachmentTotalSizeLimit)
  405. require.Equal(t, 24*time.Hour, ben.Tier.AttachmentExpiryDuration)
  406. benGrants, err := a.Grants("ben")
  407. require.Nil(t, err)
  408. require.Equal(t, 1, len(benGrants))
  409. require.Equal(t, PermissionReadWrite, benGrants[0].Permission)
  410. everyoneGrants, err := a.Grants(Everyone)
  411. require.Nil(t, err)
  412. require.Equal(t, 1, len(everyoneGrants))
  413. require.Equal(t, PermissionDenyAll, everyoneGrants[0].Permission)
  414. benReservations, err := a.Reservations("ben")
  415. require.Nil(t, err)
  416. require.Equal(t, 1, len(benReservations))
  417. require.Equal(t, "mytopic", benReservations[0].Topic)
  418. require.Equal(t, PermissionReadWrite, benReservations[0].Owner)
  419. require.Equal(t, PermissionDenyAll, benReservations[0].Everyone)
  420. // Switch to admin, this should remove all grants and owned ACL entries
  421. require.Nil(t, a.ChangeRole("ben", RoleAdmin))
  422. benGrants, err = a.Grants("ben")
  423. require.Nil(t, err)
  424. require.Equal(t, 0, len(benGrants))
  425. everyoneGrants, err = a.Grants(Everyone)
  426. require.Nil(t, err)
  427. require.Equal(t, 0, len(everyoneGrants))
  428. }
  429. func TestManager_Token_Valid(t *testing.T) {
  430. a := newTestManager(t, PermissionDenyAll)
  431. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  432. u, err := a.User("ben")
  433. require.Nil(t, err)
  434. // Create token for user
  435. token, err := a.CreateToken(u.ID, "some label", time.Now().Add(72*time.Hour), netip.IPv4Unspecified(), false)
  436. require.Nil(t, err)
  437. require.NotEmpty(t, token.Value)
  438. require.Equal(t, "some label", token.Label)
  439. require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires.Unix())
  440. u2, err := a.AuthenticateToken(token.Value)
  441. require.Nil(t, err)
  442. require.Equal(t, u.Name, u2.Name)
  443. require.Equal(t, token.Value, u2.Token)
  444. token2, err := a.Token(u.ID, token.Value)
  445. require.Nil(t, err)
  446. require.Equal(t, token.Value, token2.Value)
  447. require.Equal(t, "some label", token2.Label)
  448. tokens, err := a.Tokens(u.ID)
  449. require.Nil(t, err)
  450. require.Equal(t, 1, len(tokens))
  451. require.Equal(t, "some label", tokens[0].Label)
  452. tokens, err = a.Tokens("u_notauser")
  453. require.Nil(t, err)
  454. require.Equal(t, 0, len(tokens))
  455. // Remove token and auth again
  456. require.Nil(t, a.RemoveToken(u2.ID, u2.Token))
  457. u3, err := a.AuthenticateToken(token.Value)
  458. require.Equal(t, ErrUnauthenticated, err)
  459. require.Nil(t, u3)
  460. tokens, err = a.Tokens(u.ID)
  461. require.Nil(t, err)
  462. require.Equal(t, 0, len(tokens))
  463. }
  464. func TestManager_Token_Invalid(t *testing.T) {
  465. a := newTestManager(t, PermissionDenyAll)
  466. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  467. u, err := a.AuthenticateToken(strings.Repeat("x", 32)) // 32 == token length
  468. require.Nil(t, u)
  469. require.Equal(t, ErrUnauthenticated, err)
  470. u, err = a.AuthenticateToken("not long enough anyway")
  471. require.Nil(t, u)
  472. require.Equal(t, ErrUnauthenticated, err)
  473. }
  474. func TestManager_Token_NotFound(t *testing.T) {
  475. a := newTestManager(t, PermissionDenyAll)
  476. _, err := a.Token("u_bla", "notfound")
  477. require.Equal(t, ErrTokenNotFound, err)
  478. }
  479. func TestManager_Token_Expire(t *testing.T) {
  480. a := newTestManager(t, PermissionDenyAll)
  481. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  482. u, err := a.User("ben")
  483. require.Nil(t, err)
  484. // Create tokens for user
  485. token1, err := a.CreateToken(u.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified(), false)
  486. require.Nil(t, err)
  487. require.NotEmpty(t, token1.Value)
  488. require.True(t, time.Now().Add(71*time.Hour).Unix() < token1.Expires.Unix())
  489. token2, err := a.CreateToken(u.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified(), false)
  490. require.Nil(t, err)
  491. require.NotEmpty(t, token2.Value)
  492. require.NotEqual(t, token1.Value, token2.Value)
  493. require.True(t, time.Now().Add(71*time.Hour).Unix() < token2.Expires.Unix())
  494. // See that tokens work
  495. _, err = a.AuthenticateToken(token1.Value)
  496. require.Nil(t, err)
  497. _, err = a.AuthenticateToken(token2.Value)
  498. require.Nil(t, err)
  499. // Modify token expiration in database
  500. _, err = a.db.Exec("UPDATE user_token SET expires = 1 WHERE token = ?", token1.Value)
  501. require.Nil(t, err)
  502. // Now token1 shouldn't work anymore
  503. _, err = a.AuthenticateToken(token1.Value)
  504. require.Equal(t, ErrUnauthenticated, err)
  505. result, err := a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
  506. require.Nil(t, err)
  507. require.True(t, result.Next()) // Still a matching row
  508. require.Nil(t, result.Close())
  509. // Expire tokens and check database rows
  510. require.Nil(t, a.RemoveExpiredTokens())
  511. result, err = a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
  512. require.Nil(t, err)
  513. require.False(t, result.Next()) // No matching row!
  514. require.Nil(t, result.Close())
  515. }
  516. func TestManager_Token_Extend(t *testing.T) {
  517. a := newTestManager(t, PermissionDenyAll)
  518. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  519. // Try to extend token for user without token
  520. u, err := a.User("ben")
  521. require.Nil(t, err)
  522. _, err = a.ChangeToken(u.ID, u.Token, util.String("some label"), util.Time(time.Now().Add(time.Hour)))
  523. require.Equal(t, errNoTokenProvided, err)
  524. // Create token for user
  525. token, err := a.CreateToken(u.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified(), false)
  526. require.Nil(t, err)
  527. require.NotEmpty(t, token.Value)
  528. userWithToken, err := a.AuthenticateToken(token.Value)
  529. require.Nil(t, err)
  530. extendedToken, err := a.ChangeToken(userWithToken.ID, userWithToken.Token, util.String("changed label"), util.Time(time.Now().Add(100*time.Hour)))
  531. require.Nil(t, err)
  532. require.Equal(t, token.Value, extendedToken.Value)
  533. require.Equal(t, "changed label", extendedToken.Label)
  534. require.True(t, token.Expires.Unix() < extendedToken.Expires.Unix())
  535. require.True(t, time.Now().Add(99*time.Hour).Unix() < extendedToken.Expires.Unix())
  536. }
  537. func TestManager_Token_MaxCount_AutoDelete(t *testing.T) {
  538. // Tests that tokens are automatically deleted when the maximum number of tokens is reached
  539. a := newTestManager(t, PermissionDenyAll)
  540. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  541. require.Nil(t, a.AddUser("phil", "phil", RoleUser, false))
  542. ben, err := a.User("ben")
  543. require.Nil(t, err)
  544. phil, err := a.User("phil")
  545. require.Nil(t, err)
  546. // Create 2 tokens for phil
  547. philTokens := make([]string, 0)
  548. token, err := a.CreateToken(phil.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified(), false)
  549. require.Nil(t, err)
  550. require.NotEmpty(t, token.Value)
  551. philTokens = append(philTokens, token.Value)
  552. token, err = a.CreateToken(phil.ID, "", time.Unix(0, 0), netip.IPv4Unspecified(), false)
  553. require.Nil(t, err)
  554. require.NotEmpty(t, token.Value)
  555. philTokens = append(philTokens, token.Value)
  556. // Create 62 tokens for ben (only 60 allowed!)
  557. baseTime := time.Now().Add(24 * time.Hour)
  558. benTokens := make([]string, 0)
  559. for i := 0; i < 62; i++ { //
  560. token, err := a.CreateToken(ben.ID, "", time.Now().Add(72*time.Hour), netip.IPv4Unspecified(), false)
  561. require.Nil(t, err)
  562. require.NotEmpty(t, token.Value)
  563. benTokens = append(benTokens, token.Value)
  564. // Manually modify expiry date to avoid sorting issues (this is a hack)
  565. _, err = a.db.Exec(`UPDATE user_token SET expires=? WHERE token=?`, baseTime.Add(time.Duration(i)*time.Minute).Unix(), token.Value)
  566. require.Nil(t, err)
  567. }
  568. // Ben: The first 2 tokens should have been wiped and should not work anymore!
  569. _, err = a.AuthenticateToken(benTokens[0])
  570. require.Equal(t, ErrUnauthenticated, err)
  571. _, err = a.AuthenticateToken(benTokens[1])
  572. require.Equal(t, ErrUnauthenticated, err)
  573. // Ben: The other tokens should still work
  574. for i := 2; i < 62; i++ {
  575. userWithToken, err := a.AuthenticateToken(benTokens[i])
  576. require.Nil(t, err, "token[%d]=%s failed", i, benTokens[i])
  577. require.Equal(t, "ben", userWithToken.Name)
  578. require.Equal(t, benTokens[i], userWithToken.Token)
  579. }
  580. // Phil: All tokens should still work
  581. for i := 0; i < 2; i++ {
  582. userWithToken, err := a.AuthenticateToken(philTokens[i])
  583. require.Nil(t, err, "token[%d]=%s failed", i, philTokens[i])
  584. require.Equal(t, "phil", userWithToken.Name)
  585. require.Equal(t, philTokens[i], userWithToken.Token)
  586. }
  587. var benCount int
  588. rows, err := a.db.Query(`SELECT COUNT(*) FROM user_token WHERE user_id=?`, ben.ID)
  589. require.Nil(t, err)
  590. require.True(t, rows.Next())
  591. require.Nil(t, rows.Scan(&benCount))
  592. require.Equal(t, 60, benCount)
  593. var philCount int
  594. rows, err = a.db.Query(`SELECT COUNT(*) FROM user_token WHERE user_id=?`, phil.ID)
  595. require.Nil(t, err)
  596. require.True(t, rows.Next())
  597. require.Nil(t, rows.Scan(&philCount))
  598. require.Equal(t, 2, philCount)
  599. }
  600. func TestManager_EnqueueStats_ResetStats(t *testing.T) {
  601. conf := &Config{
  602. Filename: filepath.Join(t.TempDir(), "db"),
  603. StartupQueries: "",
  604. DefaultAccess: PermissionReadWrite,
  605. BcryptCost: bcrypt.MinCost,
  606. QueueWriterInterval: 1500 * time.Millisecond,
  607. }
  608. a, err := NewManager(conf)
  609. require.Nil(t, err)
  610. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  611. // Baseline: No messages or emails
  612. u, err := a.User("ben")
  613. require.Nil(t, err)
  614. require.Equal(t, int64(0), u.Stats.Messages)
  615. require.Equal(t, int64(0), u.Stats.Emails)
  616. a.EnqueueUserStats(u.ID, &Stats{
  617. Messages: 11,
  618. Emails: 2,
  619. })
  620. // Still no change, because it's queued asynchronously
  621. u, err = a.User("ben")
  622. require.Nil(t, err)
  623. require.Equal(t, int64(0), u.Stats.Messages)
  624. require.Equal(t, int64(0), u.Stats.Emails)
  625. // After 2 seconds they should be persisted
  626. time.Sleep(2 * time.Second)
  627. u, err = a.User("ben")
  628. require.Nil(t, err)
  629. require.Equal(t, int64(11), u.Stats.Messages)
  630. require.Equal(t, int64(2), u.Stats.Emails)
  631. // Now reset stats (enqueued stats will be thrown out)
  632. a.EnqueueUserStats(u.ID, &Stats{
  633. Messages: 99,
  634. Emails: 23,
  635. })
  636. require.Nil(t, a.ResetStats())
  637. u, err = a.User("ben")
  638. require.Nil(t, err)
  639. require.Equal(t, int64(0), u.Stats.Messages)
  640. require.Equal(t, int64(0), u.Stats.Emails)
  641. }
  642. func TestManager_EnqueueTokenUpdate(t *testing.T) {
  643. conf := &Config{
  644. Filename: filepath.Join(t.TempDir(), "db"),
  645. StartupQueries: "",
  646. DefaultAccess: PermissionReadWrite,
  647. BcryptCost: bcrypt.MinCost,
  648. QueueWriterInterval: 500 * time.Millisecond,
  649. }
  650. a, err := NewManager(conf)
  651. require.Nil(t, err)
  652. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  653. // Create user and token
  654. u, err := a.User("ben")
  655. require.Nil(t, err)
  656. token, err := a.CreateToken(u.ID, "", time.Now().Add(time.Hour), netip.IPv4Unspecified(), false)
  657. require.Nil(t, err)
  658. // Queue token update
  659. a.EnqueueTokenUpdate(token.Value, &TokenUpdate{
  660. LastAccess: time.Unix(111, 0).UTC(),
  661. LastOrigin: netip.MustParseAddr("1.2.3.3"),
  662. })
  663. // Token has not changed yet.
  664. token2, err := a.Token(u.ID, token.Value)
  665. require.Nil(t, err)
  666. require.Equal(t, token.LastAccess.Unix(), token2.LastAccess.Unix())
  667. require.Equal(t, token.LastOrigin, token2.LastOrigin)
  668. // After a second or so they should be persisted
  669. time.Sleep(time.Second)
  670. token3, err := a.Token(u.ID, token.Value)
  671. require.Nil(t, err)
  672. require.Equal(t, time.Unix(111, 0).UTC().Unix(), token3.LastAccess.Unix())
  673. require.Equal(t, netip.MustParseAddr("1.2.3.3"), token3.LastOrigin)
  674. }
  675. func TestManager_ChangeSettings(t *testing.T) {
  676. conf := &Config{
  677. Filename: filepath.Join(t.TempDir(), "db"),
  678. StartupQueries: "",
  679. DefaultAccess: PermissionReadWrite,
  680. BcryptCost: bcrypt.MinCost,
  681. QueueWriterInterval: 1500 * time.Millisecond,
  682. }
  683. a, err := NewManager(conf)
  684. require.Nil(t, err)
  685. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  686. // No settings
  687. u, err := a.User("ben")
  688. require.Nil(t, err)
  689. require.Nil(t, u.Prefs.Subscriptions)
  690. require.Nil(t, u.Prefs.Notification)
  691. require.Nil(t, u.Prefs.Language)
  692. // Save with new settings
  693. prefs := &Prefs{
  694. Language: util.String("de"),
  695. Notification: &NotificationPrefs{
  696. Sound: util.String("ding"),
  697. MinPriority: util.Int(2),
  698. },
  699. Subscriptions: []*Subscription{
  700. {
  701. BaseURL: "https://ntfy.sh",
  702. Topic: "mytopic",
  703. DisplayName: util.String("My Topic"),
  704. },
  705. },
  706. }
  707. require.Nil(t, a.ChangeSettings(u.ID, prefs))
  708. // Read again
  709. u, err = a.User("ben")
  710. require.Nil(t, err)
  711. require.Equal(t, util.String("de"), u.Prefs.Language)
  712. require.Equal(t, util.String("ding"), u.Prefs.Notification.Sound)
  713. require.Equal(t, util.Int(2), u.Prefs.Notification.MinPriority)
  714. require.Nil(t, u.Prefs.Notification.DeleteAfter)
  715. require.Equal(t, "https://ntfy.sh", u.Prefs.Subscriptions[0].BaseURL)
  716. require.Equal(t, "mytopic", u.Prefs.Subscriptions[0].Topic)
  717. require.Equal(t, util.String("My Topic"), u.Prefs.Subscriptions[0].DisplayName)
  718. }
  719. func TestManager_Tier_Create_Update_List_Delete(t *testing.T) {
  720. a := newTestManager(t, PermissionDenyAll)
  721. // Create tier and user
  722. require.Nil(t, a.AddTier(&Tier{
  723. Code: "supporter",
  724. Name: "Supporter",
  725. MessageLimit: 1,
  726. MessageExpiryDuration: time.Second,
  727. EmailLimit: 1,
  728. ReservationLimit: 1,
  729. AttachmentFileSizeLimit: 1,
  730. AttachmentTotalSizeLimit: 1,
  731. AttachmentExpiryDuration: time.Second,
  732. AttachmentBandwidthLimit: 1,
  733. StripeMonthlyPriceID: "price_1",
  734. }))
  735. require.Nil(t, a.AddTier(&Tier{
  736. Code: "pro",
  737. Name: "Pro",
  738. MessageLimit: 123,
  739. MessageExpiryDuration: 86400 * time.Second,
  740. EmailLimit: 32,
  741. ReservationLimit: 2,
  742. AttachmentFileSizeLimit: 1231231,
  743. AttachmentTotalSizeLimit: 123123,
  744. AttachmentExpiryDuration: 10800 * time.Second,
  745. AttachmentBandwidthLimit: 21474836480,
  746. StripeMonthlyPriceID: "price_2",
  747. }))
  748. require.Nil(t, a.AddUser("phil", "phil", RoleUser, false))
  749. require.Nil(t, a.ChangeTier("phil", "pro"))
  750. ti, err := a.Tier("pro")
  751. require.Nil(t, err)
  752. u, err := a.User("phil")
  753. require.Nil(t, err)
  754. // These are populated by different SQL queries
  755. require.Equal(t, ti, u.Tier)
  756. // Fields
  757. require.True(t, strings.HasPrefix(ti.ID, "ti_"))
  758. require.Equal(t, "pro", ti.Code)
  759. require.Equal(t, "Pro", ti.Name)
  760. require.Equal(t, int64(123), ti.MessageLimit)
  761. require.Equal(t, 86400*time.Second, ti.MessageExpiryDuration)
  762. require.Equal(t, int64(32), ti.EmailLimit)
  763. require.Equal(t, int64(2), ti.ReservationLimit)
  764. require.Equal(t, int64(1231231), ti.AttachmentFileSizeLimit)
  765. require.Equal(t, int64(123123), ti.AttachmentTotalSizeLimit)
  766. require.Equal(t, 10800*time.Second, ti.AttachmentExpiryDuration)
  767. require.Equal(t, int64(21474836480), ti.AttachmentBandwidthLimit)
  768. require.Equal(t, "price_2", ti.StripeMonthlyPriceID)
  769. // Update tier
  770. ti.EmailLimit = 999999
  771. require.Nil(t, a.UpdateTier(ti))
  772. // List tiers
  773. tiers, err := a.Tiers()
  774. require.Nil(t, err)
  775. require.Equal(t, 2, len(tiers))
  776. ti = tiers[0]
  777. require.Equal(t, "supporter", ti.Code)
  778. require.Equal(t, "Supporter", ti.Name)
  779. require.Equal(t, int64(1), ti.MessageLimit)
  780. require.Equal(t, time.Second, ti.MessageExpiryDuration)
  781. require.Equal(t, int64(1), ti.EmailLimit)
  782. require.Equal(t, int64(1), ti.ReservationLimit)
  783. require.Equal(t, int64(1), ti.AttachmentFileSizeLimit)
  784. require.Equal(t, int64(1), ti.AttachmentTotalSizeLimit)
  785. require.Equal(t, time.Second, ti.AttachmentExpiryDuration)
  786. require.Equal(t, int64(1), ti.AttachmentBandwidthLimit)
  787. require.Equal(t, "price_1", ti.StripeMonthlyPriceID)
  788. ti = tiers[1]
  789. require.Equal(t, "pro", ti.Code)
  790. require.Equal(t, "Pro", ti.Name)
  791. require.Equal(t, int64(123), ti.MessageLimit)
  792. require.Equal(t, 86400*time.Second, ti.MessageExpiryDuration)
  793. require.Equal(t, int64(999999), ti.EmailLimit) // Updatedd!
  794. require.Equal(t, int64(2), ti.ReservationLimit)
  795. require.Equal(t, int64(1231231), ti.AttachmentFileSizeLimit)
  796. require.Equal(t, int64(123123), ti.AttachmentTotalSizeLimit)
  797. require.Equal(t, 10800*time.Second, ti.AttachmentExpiryDuration)
  798. require.Equal(t, int64(21474836480), ti.AttachmentBandwidthLimit)
  799. require.Equal(t, "price_2", ti.StripeMonthlyPriceID)
  800. ti, err = a.TierByStripePrice("price_1")
  801. require.Nil(t, err)
  802. require.Equal(t, "supporter", ti.Code)
  803. require.Equal(t, "Supporter", ti.Name)
  804. require.Equal(t, int64(1), ti.MessageLimit)
  805. require.Equal(t, time.Second, ti.MessageExpiryDuration)
  806. require.Equal(t, int64(1), ti.EmailLimit)
  807. require.Equal(t, int64(1), ti.ReservationLimit)
  808. require.Equal(t, int64(1), ti.AttachmentFileSizeLimit)
  809. require.Equal(t, int64(1), ti.AttachmentTotalSizeLimit)
  810. require.Equal(t, time.Second, ti.AttachmentExpiryDuration)
  811. require.Equal(t, int64(1), ti.AttachmentBandwidthLimit)
  812. require.Equal(t, "price_1", ti.StripeMonthlyPriceID)
  813. // Cannot remove tier, since user has this tier
  814. require.Error(t, a.RemoveTier("pro"))
  815. // CAN remove this tier
  816. require.Nil(t, a.RemoveTier("supporter"))
  817. tiers, err = a.Tiers()
  818. require.Nil(t, err)
  819. require.Equal(t, 1, len(tiers))
  820. require.Equal(t, "pro", tiers[0].Code)
  821. require.Equal(t, "pro", tiers[0].Code)
  822. }
  823. func TestAccount_Tier_Create_With_ID(t *testing.T) {
  824. a := newTestManager(t, PermissionDenyAll)
  825. require.Nil(t, a.AddTier(&Tier{
  826. ID: "ti_123",
  827. Code: "pro",
  828. }))
  829. ti, err := a.Tier("pro")
  830. require.Nil(t, err)
  831. require.Equal(t, "ti_123", ti.ID)
  832. }
  833. func TestManager_Tier_Change_And_Reset(t *testing.T) {
  834. a := newTestManager(t, PermissionDenyAll)
  835. // Create tier and user
  836. require.Nil(t, a.AddTier(&Tier{
  837. Code: "supporter",
  838. Name: "Supporter",
  839. ReservationLimit: 3,
  840. }))
  841. require.Nil(t, a.AddTier(&Tier{
  842. Code: "pro",
  843. Name: "Pro",
  844. ReservationLimit: 4,
  845. }))
  846. require.Nil(t, a.AddUser("phil", "phil", RoleUser, false))
  847. require.Nil(t, a.ChangeTier("phil", "pro"))
  848. // Add 10 reservations (pro tier allows that)
  849. for i := 0; i < 4; i++ {
  850. require.Nil(t, a.AddReservation("phil", fmt.Sprintf("topic%d", i), PermissionWrite))
  851. }
  852. // Downgrading will not work (too many reservations)
  853. require.Equal(t, ErrTooManyReservations, a.ChangeTier("phil", "supporter"))
  854. // Downgrade after removing a reservation
  855. require.Nil(t, a.RemoveReservations("phil", "topic0"))
  856. require.Nil(t, a.ChangeTier("phil", "supporter"))
  857. // Resetting will not work (too many reservations)
  858. require.Equal(t, ErrTooManyReservations, a.ResetTier("phil"))
  859. // Resetting after removing all reservations
  860. require.Nil(t, a.RemoveReservations("phil", "topic1", "topic2", "topic3"))
  861. require.Nil(t, a.ResetTier("phil"))
  862. }
  863. func TestUser_PhoneNumberAddListRemove(t *testing.T) {
  864. a := newTestManager(t, PermissionDenyAll)
  865. require.Nil(t, a.AddUser("phil", "phil", RoleUser, false))
  866. phil, err := a.User("phil")
  867. require.Nil(t, err)
  868. require.Nil(t, a.AddPhoneNumber(phil.ID, "+1234567890"))
  869. phoneNumbers, err := a.PhoneNumbers(phil.ID)
  870. require.Nil(t, err)
  871. require.Equal(t, 1, len(phoneNumbers))
  872. require.Equal(t, "+1234567890", phoneNumbers[0])
  873. require.Nil(t, a.RemovePhoneNumber(phil.ID, "+1234567890"))
  874. phoneNumbers, err = a.PhoneNumbers(phil.ID)
  875. require.Nil(t, err)
  876. require.Equal(t, 0, len(phoneNumbers))
  877. // Paranoia check: We do NOT want to keep phone numbers in there
  878. rows, err := a.db.Query(`SELECT * FROM user_phone`)
  879. require.Nil(t, err)
  880. require.False(t, rows.Next())
  881. require.Nil(t, rows.Close())
  882. }
  883. func TestUser_PhoneNumberAdd_Multiple_Users_Same_Number(t *testing.T) {
  884. a := newTestManager(t, PermissionDenyAll)
  885. require.Nil(t, a.AddUser("phil", "phil", RoleUser, false))
  886. require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
  887. phil, err := a.User("phil")
  888. require.Nil(t, err)
  889. ben, err := a.User("ben")
  890. require.Nil(t, err)
  891. require.Nil(t, a.AddPhoneNumber(phil.ID, "+1234567890"))
  892. require.Nil(t, a.AddPhoneNumber(ben.ID, "+1234567890"))
  893. }
  894. func TestManager_Topic_Wildcard_With_Asterisk_Underscore(t *testing.T) {
  895. f := filepath.Join(t.TempDir(), "user.db")
  896. a := newTestManagerFromFile(t, f, "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
  897. require.Nil(t, a.AllowAccess(Everyone, "*_", PermissionRead))
  898. require.Nil(t, a.AllowAccess(Everyone, "__*_", PermissionRead))
  899. require.Nil(t, a.Authorize(nil, "allowed_", PermissionRead))
  900. require.Nil(t, a.Authorize(nil, "__allowed_", PermissionRead))
  901. require.Nil(t, a.Authorize(nil, "_allowed_", PermissionRead)) // The "%" in "%\_" matches the first "_"
  902. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "notallowed", PermissionRead))
  903. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "_notallowed", PermissionRead))
  904. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "__notallowed", PermissionRead))
  905. }
  906. func TestManager_Topic_Wildcard_With_Underscore(t *testing.T) {
  907. f := filepath.Join(t.TempDir(), "user.db")
  908. a := newTestManagerFromFile(t, f, "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
  909. require.Nil(t, a.AllowAccess(Everyone, "mytopic_", PermissionReadWrite))
  910. require.Nil(t, a.Authorize(nil, "mytopic_", PermissionRead))
  911. require.Nil(t, a.Authorize(nil, "mytopic_", PermissionWrite))
  912. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopicX", PermissionRead))
  913. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopicX", PermissionWrite))
  914. }
  915. func TestManager_WithProvisionedUsers(t *testing.T) {
  916. f := filepath.Join(t.TempDir(), "user.db")
  917. conf := &Config{
  918. Filename: f,
  919. DefaultAccess: PermissionReadWrite,
  920. ProvisionEnabled: true,
  921. Users: []*User{
  922. {Name: "philuser", Hash: "$2a$10$YLiO8U21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C", Role: RoleUser},
  923. {Name: "philadmin", Hash: "$2a$10$YLiO8U21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C", Role: RoleAdmin},
  924. },
  925. Access: map[string][]*Grant{
  926. "philuser": {
  927. {TopicPattern: "stats", Permission: PermissionReadWrite},
  928. {TopicPattern: "secret", Permission: PermissionRead},
  929. },
  930. },
  931. Tokens: map[string][]*Token{
  932. "philuser": {
  933. {Value: "tk_op56p8lz5bf3cxkz9je99v9oc37lo", Label: "Alerts token"},
  934. },
  935. },
  936. }
  937. a, err := NewManager(conf)
  938. require.Nil(t, err)
  939. // Manually add user
  940. require.Nil(t, a.AddUser("philmanual", "manual", RoleUser, false))
  941. // Check that the provisioned users are there
  942. users, err := a.Users()
  943. require.Nil(t, err)
  944. require.Len(t, users, 4)
  945. require.Equal(t, "philadmin", users[0].Name)
  946. require.Equal(t, RoleAdmin, users[0].Role)
  947. require.Equal(t, "philmanual", users[1].Name)
  948. require.Equal(t, RoleUser, users[1].Role)
  949. require.Equal(t, "philuser", users[2].Name)
  950. require.Equal(t, RoleUser, users[2].Role)
  951. require.Equal(t, "*", users[3].Name)
  952. provisionedUserID := users[2].ID // "philuser" is the provisioned user
  953. grants, err := a.Grants("philuser")
  954. require.Nil(t, err)
  955. require.Equal(t, 2, len(grants))
  956. require.Equal(t, "secret", grants[0].TopicPattern)
  957. require.Equal(t, PermissionRead, grants[0].Permission)
  958. require.Equal(t, "stats", grants[1].TopicPattern)
  959. require.Equal(t, PermissionReadWrite, grants[1].Permission)
  960. tokens, err := a.Tokens(provisionedUserID)
  961. require.Nil(t, err)
  962. require.Equal(t, 1, len(tokens))
  963. require.Equal(t, "tk_op56p8lz5bf3cxkz9je99v9oc37lo", tokens[0].Value)
  964. require.Equal(t, "Alerts token", tokens[0].Label)
  965. require.True(t, tokens[0].Provisioned)
  966. // Update the token last access time and origin (so we can check that it is persisted)
  967. lastAccessTime := time.Now().Add(time.Hour)
  968. lastOrigin := netip.MustParseAddr("1.1.9.9")
  969. err = execTx(a.db, func(tx *sql.Tx) error {
  970. return a.updateTokenLastAccessTx(tx, tokens[0].Value, lastAccessTime.Unix(), lastOrigin.String())
  971. })
  972. require.Nil(t, err)
  973. // Re-open the DB (second app start)
  974. require.Nil(t, a.db.Close())
  975. conf.Users = []*User{
  976. {Name: "philuser", Hash: "$2a$10$AAAAU21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C", Role: RoleUser},
  977. }
  978. conf.Access = map[string][]*Grant{
  979. "philuser": {
  980. {TopicPattern: "stats12", Permission: PermissionReadWrite},
  981. {TopicPattern: "secret12", Permission: PermissionRead},
  982. },
  983. }
  984. conf.Tokens = map[string][]*Token{
  985. "philuser": {
  986. {Value: "tk_op56p8lz5bf3cxkz9je99v9oc37lo", Label: "Alerts token updated"},
  987. {Value: "tk_u48wqendnkx9er21pqqcadlytbutx", Label: "Another token"},
  988. },
  989. }
  990. a, err = NewManager(conf)
  991. require.Nil(t, err)
  992. // Check that the provisioned users are there
  993. users, err = a.Users()
  994. require.Nil(t, err)
  995. require.Len(t, users, 3)
  996. require.Equal(t, "philmanual", users[0].Name)
  997. require.Equal(t, "philuser", users[1].Name)
  998. require.Equal(t, RoleUser, users[1].Role)
  999. require.Equal(t, RoleUser, users[0].Role)
  1000. require.Equal(t, "*", users[2].Name)
  1001. grants, err = a.Grants("philuser")
  1002. require.Nil(t, err)
  1003. require.Equal(t, 2, len(grants))
  1004. require.Equal(t, "secret12", grants[0].TopicPattern)
  1005. require.Equal(t, PermissionRead, grants[0].Permission)
  1006. require.Equal(t, "stats12", grants[1].TopicPattern)
  1007. require.Equal(t, PermissionReadWrite, grants[1].Permission)
  1008. tokens, err = a.Tokens(provisionedUserID)
  1009. require.Nil(t, err)
  1010. require.Equal(t, 2, len(tokens))
  1011. require.Equal(t, "tk_op56p8lz5bf3cxkz9je99v9oc37lo", tokens[0].Value)
  1012. require.Equal(t, "Alerts token updated", tokens[0].Label)
  1013. require.Equal(t, lastAccessTime.Unix(), tokens[0].LastAccess.Unix())
  1014. require.Equal(t, lastOrigin, tokens[0].LastOrigin)
  1015. require.True(t, tokens[0].Provisioned)
  1016. require.Equal(t, "tk_u48wqendnkx9er21pqqcadlytbutx", tokens[1].Value)
  1017. require.Equal(t, "Another token", tokens[1].Label)
  1018. // Try changing provisioned user's password
  1019. require.Error(t, a.ChangePassword("philuser", "new-pass", false))
  1020. // Re-open the DB again (third app start)
  1021. require.Nil(t, a.db.Close())
  1022. conf.Users = []*User{}
  1023. conf.Access = map[string][]*Grant{}
  1024. conf.Tokens = map[string][]*Token{}
  1025. a, err = NewManager(conf)
  1026. require.Nil(t, err)
  1027. // Check that the provisioned users are all gone
  1028. users, err = a.Users()
  1029. require.Nil(t, err)
  1030. require.Len(t, users, 2)
  1031. require.Equal(t, "philmanual", users[0].Name)
  1032. require.Equal(t, RoleUser, users[0].Role)
  1033. require.Equal(t, "*", users[1].Name)
  1034. grants, err = a.Grants("philuser")
  1035. require.Nil(t, err)
  1036. require.Equal(t, 0, len(grants))
  1037. tokens, err = a.Tokens(provisionedUserID)
  1038. require.Nil(t, err)
  1039. require.Equal(t, 0, len(tokens))
  1040. var count int
  1041. a.db.QueryRow("SELECT COUNT(*) FROM user WHERE provisioned = 1").Scan(&count)
  1042. require.Equal(t, 0, count)
  1043. a.db.QueryRow("SELECT COUNT(*) FROM user_grant WHERE provisioned = 1").Scan(&count)
  1044. require.Equal(t, 0, count)
  1045. a.db.QueryRow("SELECT COUNT(*) FROM user_token WHERE provisioned = 1").Scan(&count)
  1046. }
  1047. func TestManager_UpdateNonProvisionedUsersToProvisionedUsers(t *testing.T) {
  1048. f := filepath.Join(t.TempDir(), "user.db")
  1049. conf := &Config{
  1050. Filename: f,
  1051. DefaultAccess: PermissionReadWrite,
  1052. ProvisionEnabled: true,
  1053. Users: []*User{},
  1054. Access: map[string][]*Grant{
  1055. Everyone: {
  1056. {TopicPattern: "food", Permission: PermissionRead},
  1057. },
  1058. },
  1059. }
  1060. a, err := NewManager(conf)
  1061. require.Nil(t, err)
  1062. // Manually add user
  1063. require.Nil(t, a.AddUser("philuser", "manual", RoleUser, false))
  1064. require.Nil(t, a.AllowAccess("philuser", "stats", PermissionReadWrite))
  1065. require.Nil(t, a.AllowAccess("philuser", "food", PermissionReadWrite))
  1066. users, err := a.Users()
  1067. require.Nil(t, err)
  1068. require.Len(t, users, 2)
  1069. require.Equal(t, "philuser", users[0].Name)
  1070. require.Equal(t, RoleUser, users[0].Role)
  1071. require.False(t, users[0].Provisioned) // Manually added
  1072. grants, err := a.Grants("philuser")
  1073. require.Nil(t, err)
  1074. require.Equal(t, 2, len(grants))
  1075. require.Equal(t, "stats", grants[0].TopicPattern)
  1076. require.Equal(t, PermissionReadWrite, grants[0].Permission)
  1077. require.False(t, grants[0].Provisioned) // Manually added
  1078. require.Equal(t, "food", grants[1].TopicPattern)
  1079. require.Equal(t, PermissionReadWrite, grants[1].Permission)
  1080. require.False(t, grants[1].Provisioned) // Manually added
  1081. grants, err = a.Grants(Everyone)
  1082. require.Nil(t, err)
  1083. require.Equal(t, 1, len(grants))
  1084. require.Equal(t, "food", grants[0].TopicPattern)
  1085. require.Equal(t, PermissionRead, grants[0].Permission)
  1086. require.True(t, grants[0].Provisioned) // Provisioned entry
  1087. // Re-open the DB (second app start)
  1088. require.Nil(t, a.db.Close())
  1089. conf.Users = []*User{
  1090. {Name: "philuser", Hash: "$2a$10$AAAAU21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C", Role: RoleUser},
  1091. }
  1092. conf.Access = map[string][]*Grant{
  1093. "philuser": {
  1094. {TopicPattern: "stats", Permission: PermissionReadWrite},
  1095. },
  1096. }
  1097. a, err = NewManager(conf)
  1098. require.Nil(t, err)
  1099. // Check that the user was "upgraded" to a provisioned user
  1100. users, err = a.Users()
  1101. require.Nil(t, err)
  1102. require.Len(t, users, 2)
  1103. require.Equal(t, "philuser", users[0].Name)
  1104. require.Equal(t, RoleUser, users[0].Role)
  1105. require.Equal(t, "$2a$10$AAAAU21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C", users[0].Hash)
  1106. require.True(t, users[0].Provisioned) // Updated to provisioned!
  1107. grants, err = a.Grants("philuser")
  1108. require.Nil(t, err)
  1109. require.Equal(t, 2, len(grants))
  1110. require.Equal(t, "stats", grants[0].TopicPattern)
  1111. require.Equal(t, PermissionReadWrite, grants[0].Permission)
  1112. require.True(t, grants[0].Provisioned) // Updated to provisioned!
  1113. require.Equal(t, "food", grants[1].TopicPattern)
  1114. require.Equal(t, PermissionReadWrite, grants[1].Permission)
  1115. require.False(t, grants[1].Provisioned) // Manually added grants stay!
  1116. grants, err = a.Grants(Everyone)
  1117. require.Nil(t, err)
  1118. require.Empty(t, grants)
  1119. }
  1120. func TestToFromSQLWildcard(t *testing.T) {
  1121. require.Equal(t, "up%", toSQLWildcard("up*"))
  1122. require.Equal(t, "up\\_%", toSQLWildcard("up_*"))
  1123. require.Equal(t, "foo", toSQLWildcard("foo"))
  1124. require.Equal(t, "up*", fromSQLWildcard("up%"))
  1125. require.Equal(t, "up_*", fromSQLWildcard("up\\_%"))
  1126. require.Equal(t, "foo", fromSQLWildcard("foo"))
  1127. require.Equal(t, "up*", fromSQLWildcard(toSQLWildcard("up*")))
  1128. require.Equal(t, "up_*", fromSQLWildcard(toSQLWildcard("up_*")))
  1129. require.Equal(t, "foo", fromSQLWildcard(toSQLWildcard("foo")))
  1130. }
  1131. func TestMigrationFrom1(t *testing.T) {
  1132. filename := filepath.Join(t.TempDir(), "user.db")
  1133. db, err := sql.Open("sqlite3", filename)
  1134. require.Nil(t, err)
  1135. // Create "version 1" schema
  1136. _, err = db.Exec(`
  1137. BEGIN;
  1138. CREATE TABLE IF NOT EXISTS user (
  1139. user TEXT NOT NULL PRIMARY KEY,
  1140. pass TEXT NOT NULL,
  1141. role TEXT NOT NULL
  1142. );
  1143. CREATE TABLE IF NOT EXISTS access (
  1144. user TEXT NOT NULL,
  1145. topic TEXT NOT NULL,
  1146. read INT NOT NULL,
  1147. write INT NOT NULL,
  1148. PRIMARY KEY (topic, user)
  1149. );
  1150. CREATE TABLE IF NOT EXISTS schemaVersion (
  1151. id INT PRIMARY KEY,
  1152. version INT NOT NULL
  1153. );
  1154. INSERT INTO schemaVersion (id, version) VALUES (1, 1);
  1155. COMMIT;
  1156. `)
  1157. require.Nil(t, err)
  1158. // Insert a bunch of users and ACL entries
  1159. _, err = db.Exec(`
  1160. BEGIN;
  1161. INSERT INTO user (user, pass, role) VALUES ('ben', '$2a$10$EEp6gBheOsqEFsXlo523E.gBVoeg1ytphXiEvTPlNzkenBlHZBPQy', 'user');
  1162. INSERT INTO user (user, pass, role) VALUES ('phil', '$2a$10$YLiO8U21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C', 'admin');
  1163. INSERT INTO access (user, topic, read, write) VALUES ('ben', 'stats', 1, 1);
  1164. INSERT INTO access (user, topic, read, write) VALUES ('ben', 'secret', 1, 0);
  1165. INSERT INTO access (user, topic, read, write) VALUES ('*', 'stats', 1, 0);
  1166. COMMIT;
  1167. `)
  1168. require.Nil(t, err)
  1169. // Create manager to trigger migration
  1170. a := newTestManagerFromFile(t, filename, "", PermissionDenyAll, bcrypt.MinCost, DefaultUserStatsQueueWriterInterval)
  1171. checkSchemaVersion(t, a.db)
  1172. users, err := a.Users()
  1173. require.Nil(t, err)
  1174. require.Equal(t, 3, len(users))
  1175. phil, ben, everyone := users[0], users[1], users[2]
  1176. philGrants, err := a.Grants("phil")
  1177. require.Nil(t, err)
  1178. benGrants, err := a.Grants("ben")
  1179. require.Nil(t, err)
  1180. everyoneGrants, err := a.Grants(Everyone)
  1181. require.Nil(t, err)
  1182. require.True(t, strings.HasPrefix(phil.ID, "u_"))
  1183. require.Equal(t, "phil", phil.Name)
  1184. require.Equal(t, RoleAdmin, phil.Role)
  1185. require.Equal(t, syncTopicLength, len(phil.SyncTopic))
  1186. require.Equal(t, 0, len(philGrants))
  1187. require.True(t, strings.HasPrefix(ben.ID, "u_"))
  1188. require.NotEqual(t, phil.ID, ben.ID)
  1189. require.Equal(t, "ben", ben.Name)
  1190. require.Equal(t, RoleUser, ben.Role)
  1191. require.Equal(t, syncTopicLength, len(ben.SyncTopic))
  1192. require.NotEqual(t, ben.SyncTopic, phil.SyncTopic)
  1193. require.Equal(t, 2, len(benGrants))
  1194. require.Equal(t, "secret", benGrants[0].TopicPattern)
  1195. require.Equal(t, PermissionRead, benGrants[0].Permission)
  1196. require.Equal(t, "stats", benGrants[1].TopicPattern)
  1197. require.Equal(t, PermissionReadWrite, benGrants[1].Permission)
  1198. require.Equal(t, "u_everyone", everyone.ID)
  1199. require.Equal(t, Everyone, everyone.Name)
  1200. require.Equal(t, RoleAnonymous, everyone.Role)
  1201. require.Equal(t, 1, len(everyoneGrants))
  1202. require.Equal(t, "stats", everyoneGrants[0].TopicPattern)
  1203. require.Equal(t, PermissionRead, everyoneGrants[0].Permission)
  1204. }
  1205. func TestMigrationFrom4(t *testing.T) {
  1206. filename := filepath.Join(t.TempDir(), "user.db")
  1207. db, err := sql.Open("sqlite3", filename)
  1208. require.Nil(t, err)
  1209. // Create "version 4" schema
  1210. _, err = db.Exec(`
  1211. BEGIN;
  1212. CREATE TABLE IF NOT EXISTS tier (
  1213. id TEXT PRIMARY KEY,
  1214. code TEXT NOT NULL,
  1215. name TEXT NOT NULL,
  1216. messages_limit INT NOT NULL,
  1217. messages_expiry_duration INT NOT NULL,
  1218. emails_limit INT NOT NULL,
  1219. calls_limit INT NOT NULL,
  1220. reservations_limit INT NOT NULL,
  1221. attachment_file_size_limit INT NOT NULL,
  1222. attachment_total_size_limit INT NOT NULL,
  1223. attachment_expiry_duration INT NOT NULL,
  1224. attachment_bandwidth_limit INT NOT NULL,
  1225. stripe_monthly_price_id TEXT,
  1226. stripe_yearly_price_id TEXT
  1227. );
  1228. CREATE UNIQUE INDEX idx_tier_code ON tier (code);
  1229. CREATE UNIQUE INDEX idx_tier_stripe_monthly_price_id ON tier (stripe_monthly_price_id);
  1230. CREATE UNIQUE INDEX idx_tier_stripe_yearly_price_id ON tier (stripe_yearly_price_id);
  1231. CREATE TABLE IF NOT EXISTS user (
  1232. id TEXT PRIMARY KEY,
  1233. tier_id TEXT,
  1234. user TEXT NOT NULL,
  1235. pass TEXT NOT NULL,
  1236. role TEXT CHECK (role IN ('anonymous', 'admin', 'user')) NOT NULL,
  1237. prefs JSON NOT NULL DEFAULT '{}',
  1238. sync_topic TEXT NOT NULL,
  1239. stats_messages INT NOT NULL DEFAULT (0),
  1240. stats_emails INT NOT NULL DEFAULT (0),
  1241. stats_calls INT NOT NULL DEFAULT (0),
  1242. stripe_customer_id TEXT,
  1243. stripe_subscription_id TEXT,
  1244. stripe_subscription_status TEXT,
  1245. stripe_subscription_interval TEXT,
  1246. stripe_subscription_paid_until INT,
  1247. stripe_subscription_cancel_at INT,
  1248. created INT NOT NULL,
  1249. deleted INT,
  1250. FOREIGN KEY (tier_id) REFERENCES tier (id)
  1251. );
  1252. CREATE UNIQUE INDEX idx_user ON user (user);
  1253. CREATE UNIQUE INDEX idx_user_stripe_customer_id ON user (stripe_customer_id);
  1254. CREATE UNIQUE INDEX idx_user_stripe_subscription_id ON user (stripe_subscription_id);
  1255. CREATE TABLE IF NOT EXISTS user_access (
  1256. user_id TEXT NOT NULL,
  1257. topic TEXT NOT NULL,
  1258. read INT NOT NULL,
  1259. write INT NOT NULL,
  1260. owner_user_id INT,
  1261. PRIMARY KEY (user_id, topic),
  1262. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE,
  1263. FOREIGN KEY (owner_user_id) REFERENCES user (id) ON DELETE CASCADE
  1264. );
  1265. CREATE TABLE IF NOT EXISTS user_token (
  1266. user_id TEXT NOT NULL,
  1267. token TEXT NOT NULL,
  1268. label TEXT NOT NULL,
  1269. last_access INT NOT NULL,
  1270. last_origin TEXT NOT NULL,
  1271. expires INT NOT NULL,
  1272. PRIMARY KEY (user_id, token),
  1273. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
  1274. );
  1275. CREATE TABLE IF NOT EXISTS user_phone (
  1276. user_id TEXT NOT NULL,
  1277. phone_number TEXT NOT NULL,
  1278. PRIMARY KEY (user_id, phone_number),
  1279. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
  1280. );
  1281. CREATE TABLE IF NOT EXISTS schemaVersion (
  1282. id INT PRIMARY KEY,
  1283. version INT NOT NULL
  1284. );
  1285. INSERT INTO user (id, user, pass, role, sync_topic, created)
  1286. VALUES ('u_everyone', '*', '', 'anonymous', '', UNIXEPOCH())
  1287. ON CONFLICT (id) DO NOTHING;
  1288. INSERT INTO schemaVersion (id, version) VALUES (1, 4);
  1289. COMMIT;
  1290. `)
  1291. require.Nil(t, err)
  1292. // Insert a few ACL entries
  1293. _, err = db.Exec(`
  1294. BEGIN;
  1295. INSERT INTO user_access (user_id, topic, read, write) values ('u_everyone', 'mytopic_', 1, 1);
  1296. INSERT INTO user_access (user_id, topic, read, write) values ('u_everyone', 'up%', 1, 1);
  1297. INSERT INTO user_access (user_id, topic, read, write) values ('u_everyone', 'down_%', 1, 1);
  1298. COMMIT;
  1299. `)
  1300. require.Nil(t, err)
  1301. // Create manager to trigger migration
  1302. a := newTestManagerFromFile(t, filename, "", PermissionDenyAll, bcrypt.MinCost, DefaultUserStatsQueueWriterInterval)
  1303. checkSchemaVersion(t, a.db)
  1304. // Add another
  1305. require.Nil(t, a.AllowAccess(Everyone, "left_*", PermissionReadWrite))
  1306. // Check "external view" of grants
  1307. everyoneGrants, err := a.Grants(Everyone)
  1308. require.Nil(t, err)
  1309. require.Equal(t, 4, len(everyoneGrants))
  1310. require.Equal(t, "mytopic_", everyoneGrants[0].TopicPattern)
  1311. require.Equal(t, "down_*", everyoneGrants[1].TopicPattern)
  1312. require.Equal(t, "left_*", everyoneGrants[2].TopicPattern)
  1313. require.Equal(t, "up*", everyoneGrants[3].TopicPattern)
  1314. // Check they are stored correctly in the database
  1315. rows, err := db.Query(`SELECT topic FROM user_access WHERE user_id = 'u_everyone' ORDER BY topic`)
  1316. require.Nil(t, err)
  1317. topicPatterns := make([]string, 0)
  1318. for rows.Next() {
  1319. var topicPattern string
  1320. require.Nil(t, rows.Scan(&topicPattern))
  1321. topicPatterns = append(topicPatterns, topicPattern)
  1322. }
  1323. require.Nil(t, rows.Close())
  1324. require.Equal(t, 4, len(topicPatterns))
  1325. require.Equal(t, "down\\_%", topicPatterns[0])
  1326. require.Equal(t, "left\\_%", topicPatterns[1])
  1327. require.Equal(t, "mytopic\\_", topicPatterns[2])
  1328. require.Equal(t, "up%", topicPatterns[3])
  1329. // Check that ACL works as excepted
  1330. require.Nil(t, a.Authorize(nil, "down_123", PermissionRead))
  1331. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "downX123", PermissionRead))
  1332. require.Nil(t, a.Authorize(nil, "left_abc", PermissionRead))
  1333. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "leftX123", PermissionRead))
  1334. require.Nil(t, a.Authorize(nil, "mytopic_", PermissionRead))
  1335. require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopicX", PermissionRead))
  1336. require.Nil(t, a.Authorize(nil, "up123", PermissionRead))
  1337. require.Nil(t, a.Authorize(nil, "up", PermissionRead)) // % matches 0 or more characters
  1338. }
  1339. func checkSchemaVersion(t *testing.T, db *sql.DB) {
  1340. rows, err := db.Query(`SELECT version FROM schemaVersion`)
  1341. require.Nil(t, err)
  1342. require.True(t, rows.Next())
  1343. var schemaVersion int
  1344. require.Nil(t, rows.Scan(&schemaVersion))
  1345. require.Equal(t, currentSchemaVersion, schemaVersion)
  1346. require.Nil(t, rows.Close())
  1347. }
  1348. func newTestManager(t *testing.T, defaultAccess Permission) *Manager {
  1349. return newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", defaultAccess, bcrypt.MinCost, DefaultUserStatsQueueWriterInterval)
  1350. }
  1351. func newTestManagerFromFile(t *testing.T, filename, startupQueries string, defaultAccess Permission, bcryptCost int, statsWriterInterval time.Duration) *Manager {
  1352. conf := &Config{
  1353. Filename: filename,
  1354. StartupQueries: startupQueries,
  1355. DefaultAccess: defaultAccess,
  1356. BcryptCost: bcryptCost,
  1357. QueueWriterInterval: statsWriterInterval,
  1358. }
  1359. a, err := NewManager(conf)
  1360. require.Nil(t, err)
  1361. return a
  1362. }