server_account_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. package server
  2. import (
  3. "fmt"
  4. "github.com/stretchr/testify/require"
  5. "heckel.io/ntfy/log"
  6. "heckel.io/ntfy/user"
  7. "heckel.io/ntfy/util"
  8. "io"
  9. "net/netip"
  10. "strings"
  11. "testing"
  12. "time"
  13. )
  14. func TestAccount_Signup_Success(t *testing.T) {
  15. conf := newTestConfigWithAuthFile(t)
  16. conf.EnableSignup = true
  17. s := newTestServer(t, conf)
  18. defer s.closeDatabases()
  19. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  20. require.Equal(t, 200, rr.Code)
  21. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  22. "Authorization": util.BasicAuth("phil", "mypass"),
  23. })
  24. require.Equal(t, 200, rr.Code)
  25. token, _ := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  26. require.NotEmpty(t, token.Token)
  27. require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires)
  28. require.True(t, strings.HasPrefix(token.Token, "tk_"))
  29. require.Equal(t, "9.9.9.9", token.LastOrigin)
  30. require.True(t, token.LastAccess > time.Now().Unix()-2)
  31. require.True(t, token.LastAccess < time.Now().Unix()+2)
  32. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  33. "Authorization": util.BearerAuth(token.Token),
  34. })
  35. require.Equal(t, 200, rr.Code)
  36. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  37. require.Equal(t, "phil", account.Username)
  38. require.Equal(t, "user", account.Role)
  39. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  40. "Authorization": util.BasicAuth("", token.Token), // We allow a fake basic auth to make curl-ing easier (curl -u :<token>)
  41. })
  42. require.Equal(t, 200, rr.Code)
  43. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  44. require.Equal(t, "phil", account.Username)
  45. }
  46. func TestAccount_Signup_UserExists(t *testing.T) {
  47. conf := newTestConfigWithAuthFile(t)
  48. conf.EnableSignup = true
  49. s := newTestServer(t, conf)
  50. defer s.closeDatabases()
  51. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  52. require.Equal(t, 200, rr.Code)
  53. rr = request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  54. require.Equal(t, 409, rr.Code)
  55. require.Equal(t, 40901, toHTTPError(t, rr.Body.String()).Code)
  56. }
  57. func TestAccount_Signup_LimitReached(t *testing.T) {
  58. conf := newTestConfigWithAuthFile(t)
  59. conf.EnableSignup = true
  60. s := newTestServer(t, conf)
  61. defer s.closeDatabases()
  62. for i := 0; i < 3; i++ {
  63. rr := request(t, s, "POST", "/v1/account", fmt.Sprintf(`{"username":"phil%d", "password":"mypass"}`, i), nil)
  64. require.Equal(t, 200, rr.Code)
  65. }
  66. rr := request(t, s, "POST", "/v1/account", `{"username":"thiswontwork", "password":"mypass"}`, nil)
  67. require.Equal(t, 429, rr.Code)
  68. require.Equal(t, 42906, toHTTPError(t, rr.Body.String()).Code)
  69. }
  70. func TestAccount_Signup_AsUser(t *testing.T) {
  71. conf := newTestConfigWithAuthFile(t)
  72. conf.EnableSignup = true
  73. s := newTestServer(t, conf)
  74. defer s.closeDatabases()
  75. log.Info("1")
  76. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin))
  77. log.Info("2")
  78. require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser))
  79. log.Info("3")
  80. rr := request(t, s, "POST", "/v1/account", `{"username":"emma", "password":"emma"}`, map[string]string{
  81. "Authorization": util.BasicAuth("phil", "phil"),
  82. })
  83. require.Equal(t, 200, rr.Code)
  84. log.Info("4")
  85. rr = request(t, s, "POST", "/v1/account", `{"username":"marian", "password":"marian"}`, map[string]string{
  86. "Authorization": util.BasicAuth("ben", "ben"),
  87. })
  88. require.Equal(t, 401, rr.Code)
  89. }
  90. func TestAccount_Signup_Disabled(t *testing.T) {
  91. conf := newTestConfigWithAuthFile(t)
  92. conf.EnableSignup = false
  93. s := newTestServer(t, conf)
  94. defer s.closeDatabases()
  95. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  96. require.Equal(t, 400, rr.Code)
  97. require.Equal(t, 40022, toHTTPError(t, rr.Body.String()).Code)
  98. }
  99. func TestAccount_Signup_Rate_Limit(t *testing.T) {
  100. conf := newTestConfigWithAuthFile(t)
  101. conf.EnableSignup = true
  102. s := newTestServer(t, conf)
  103. for i := 0; i < 3; i++ {
  104. rr := request(t, s, "POST", "/v1/account", fmt.Sprintf(`{"username":"phil%d", "password":"mypass"}`, i), nil)
  105. require.Equal(t, 200, rr.Code, "failed on iteration %d", i)
  106. }
  107. rr := request(t, s, "POST", "/v1/account", `{"username":"notallowed", "password":"mypass"}`, nil)
  108. require.Equal(t, 429, rr.Code)
  109. require.Equal(t, 42906, toHTTPError(t, rr.Body.String()).Code)
  110. }
  111. func TestAccount_Get_Anonymous(t *testing.T) {
  112. conf := newTestConfigWithAuthFile(t)
  113. conf.VisitorRequestLimitReplenish = 86 * time.Second
  114. conf.VisitorEmailLimitReplenish = time.Hour
  115. conf.VisitorAttachmentTotalSizeLimit = 5123
  116. conf.AttachmentFileSizeLimit = 512
  117. s := newTestServer(t, conf)
  118. s.smtpSender = &testMailer{}
  119. defer s.closeDatabases()
  120. rr := request(t, s, "GET", "/v1/account", "", nil)
  121. require.Equal(t, 200, rr.Code)
  122. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  123. require.Equal(t, "*", account.Username)
  124. require.Equal(t, string(user.RoleAnonymous), account.Role)
  125. require.Equal(t, "ip", account.Limits.Basis)
  126. require.Equal(t, int64(1004), account.Limits.Messages) // I hate this
  127. require.Equal(t, int64(24), account.Limits.Emails) // I hate this
  128. require.Equal(t, int64(5123), account.Limits.AttachmentTotalSize)
  129. require.Equal(t, int64(512), account.Limits.AttachmentFileSize)
  130. require.Equal(t, int64(0), account.Stats.Messages)
  131. require.Equal(t, int64(1004), account.Stats.MessagesRemaining)
  132. require.Equal(t, int64(0), account.Stats.Emails)
  133. require.Equal(t, int64(24), account.Stats.EmailsRemaining)
  134. rr = request(t, s, "POST", "/mytopic", "", nil)
  135. require.Equal(t, 200, rr.Code)
  136. rr = request(t, s, "POST", "/mytopic", "", map[string]string{
  137. "Email": "phil@ntfy.sh",
  138. })
  139. require.Equal(t, 200, rr.Code)
  140. rr = request(t, s, "GET", "/v1/account", "", nil)
  141. require.Equal(t, 200, rr.Code)
  142. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  143. require.Equal(t, int64(2), account.Stats.Messages)
  144. require.Equal(t, int64(1002), account.Stats.MessagesRemaining)
  145. require.Equal(t, int64(1), account.Stats.Emails)
  146. require.Equal(t, int64(23), account.Stats.EmailsRemaining)
  147. }
  148. func TestAccount_ChangeSettings(t *testing.T) {
  149. s := newTestServer(t, newTestConfigWithAuthFile(t))
  150. defer s.closeDatabases()
  151. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  152. u, _ := s.userManager.User("phil")
  153. token, _ := s.userManager.CreateToken(u.ID, "", time.Unix(0, 0), netip.IPv4Unspecified())
  154. rr := request(t, s, "PATCH", "/v1/account/settings", `{"notification": {"sound": "juntos"},"ignored": true}`, map[string]string{
  155. "Authorization": util.BasicAuth("phil", "phil"),
  156. })
  157. require.Equal(t, 200, rr.Code)
  158. rr = request(t, s, "PATCH", "/v1/account/settings", `{"notification": {"delete_after": 86400}, "language": "de"}`, map[string]string{
  159. "Authorization": util.BearerAuth(token.Value),
  160. })
  161. require.Equal(t, 200, rr.Code)
  162. rr = request(t, s, "GET", "/v1/account", `{"username":"marian", "password":"marian"}`, map[string]string{
  163. "Authorization": util.BearerAuth(token.Value),
  164. })
  165. require.Equal(t, 200, rr.Code)
  166. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  167. require.Equal(t, "de", account.Language)
  168. require.Equal(t, util.Int(86400), account.Notification.DeleteAfter)
  169. require.Equal(t, util.String("juntos"), account.Notification.Sound)
  170. require.Nil(t, account.Notification.MinPriority) // Not set
  171. }
  172. func TestAccount_Subscription_AddUpdateDelete(t *testing.T) {
  173. s := newTestServer(t, newTestConfigWithAuthFile(t))
  174. defer s.closeDatabases()
  175. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  176. rr := request(t, s, "POST", "/v1/account/subscription", `{"base_url": "http://abc.com", "topic": "def"}`, map[string]string{
  177. "Authorization": util.BasicAuth("phil", "phil"),
  178. })
  179. require.Equal(t, 200, rr.Code)
  180. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  181. "Authorization": util.BasicAuth("phil", "phil"),
  182. })
  183. require.Equal(t, 200, rr.Code)
  184. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  185. require.Equal(t, 1, len(account.Subscriptions))
  186. require.NotEmpty(t, account.Subscriptions[0].ID)
  187. require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
  188. require.Equal(t, "def", account.Subscriptions[0].Topic)
  189. require.Nil(t, account.Subscriptions[0].DisplayName)
  190. subscriptionID := account.Subscriptions[0].ID
  191. rr = request(t, s, "PATCH", "/v1/account/subscription/"+subscriptionID, `{"display_name": "ding dong"}`, map[string]string{
  192. "Authorization": util.BasicAuth("phil", "phil"),
  193. })
  194. require.Equal(t, 200, rr.Code)
  195. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  196. "Authorization": util.BasicAuth("phil", "phil"),
  197. })
  198. require.Equal(t, 200, rr.Code)
  199. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  200. require.Equal(t, 1, len(account.Subscriptions))
  201. require.Equal(t, subscriptionID, account.Subscriptions[0].ID)
  202. require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
  203. require.Equal(t, "def", account.Subscriptions[0].Topic)
  204. require.Equal(t, util.String("ding dong"), account.Subscriptions[0].DisplayName)
  205. rr = request(t, s, "DELETE", "/v1/account/subscription/"+subscriptionID, "", map[string]string{
  206. "Authorization": util.BasicAuth("phil", "phil"),
  207. })
  208. require.Equal(t, 200, rr.Code)
  209. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  210. "Authorization": util.BasicAuth("phil", "phil"),
  211. })
  212. require.Equal(t, 200, rr.Code)
  213. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  214. require.Equal(t, 0, len(account.Subscriptions))
  215. }
  216. func TestAccount_ChangePassword(t *testing.T) {
  217. s := newTestServer(t, newTestConfigWithAuthFile(t))
  218. defer s.closeDatabases()
  219. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  220. rr := request(t, s, "POST", "/v1/account/password", `{"password": "WRONG", "new_password": ""}`, map[string]string{
  221. "Authorization": util.BasicAuth("phil", "phil"),
  222. })
  223. require.Equal(t, 400, rr.Code)
  224. rr = request(t, s, "POST", "/v1/account/password", `{"password": "WRONG", "new_password": "new password"}`, map[string]string{
  225. "Authorization": util.BasicAuth("phil", "phil"),
  226. })
  227. require.Equal(t, 400, rr.Code)
  228. require.Equal(t, 40026, toHTTPError(t, rr.Body.String()).Code)
  229. rr = request(t, s, "POST", "/v1/account/password", `{"password": "phil", "new_password": "new password"}`, map[string]string{
  230. "Authorization": util.BasicAuth("phil", "phil"),
  231. })
  232. require.Equal(t, 200, rr.Code)
  233. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  234. "Authorization": util.BasicAuth("phil", "phil"),
  235. })
  236. require.Equal(t, 401, rr.Code)
  237. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  238. "Authorization": util.BasicAuth("phil", "new password"),
  239. })
  240. require.Equal(t, 200, rr.Code)
  241. }
  242. func TestAccount_ChangePassword_NoAccount(t *testing.T) {
  243. s := newTestServer(t, newTestConfigWithAuthFile(t))
  244. defer s.closeDatabases()
  245. rr := request(t, s, "POST", "/v1/account/password", `{"password": "new password"}`, nil)
  246. require.Equal(t, 401, rr.Code)
  247. }
  248. func TestAccount_ExtendToken(t *testing.T) {
  249. s := newTestServer(t, newTestConfigWithAuthFile(t))
  250. defer s.closeDatabases()
  251. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  252. rr := request(t, s, "POST", "/v1/account/token", "", map[string]string{
  253. "Authorization": util.BasicAuth("phil", "phil"),
  254. })
  255. require.Equal(t, 200, rr.Code)
  256. token, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  257. require.Nil(t, err)
  258. time.Sleep(time.Second)
  259. rr = request(t, s, "PATCH", "/v1/account/token", "", map[string]string{
  260. "Authorization": util.BearerAuth(token.Token),
  261. })
  262. require.Equal(t, 200, rr.Code)
  263. extendedToken, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  264. require.Nil(t, err)
  265. require.Equal(t, token.Token, extendedToken.Token)
  266. require.True(t, token.Expires < extendedToken.Expires)
  267. }
  268. func TestAccount_ExtendToken_NoTokenProvided(t *testing.T) {
  269. s := newTestServer(t, newTestConfigWithAuthFile(t))
  270. defer s.closeDatabases()
  271. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  272. rr := request(t, s, "PATCH", "/v1/account/token", "", map[string]string{
  273. "Authorization": util.BasicAuth("phil", "phil"), // Not Bearer!
  274. })
  275. require.Equal(t, 400, rr.Code)
  276. require.Equal(t, 40023, toHTTPError(t, rr.Body.String()).Code)
  277. }
  278. func TestAccount_DeleteToken(t *testing.T) {
  279. s := newTestServer(t, newTestConfigWithAuthFile(t))
  280. defer s.closeDatabases()
  281. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  282. rr := request(t, s, "POST", "/v1/account/token", "", map[string]string{
  283. "Authorization": util.BasicAuth("phil", "phil"),
  284. })
  285. require.Equal(t, 200, rr.Code)
  286. token, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  287. require.Nil(t, err)
  288. require.True(t, token.Expires > time.Now().Add(71*time.Hour).Unix())
  289. // Delete token failure (using basic auth)
  290. rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{
  291. "Authorization": util.BasicAuth("phil", "phil"), // Not Bearer!
  292. })
  293. require.Equal(t, 400, rr.Code)
  294. require.Equal(t, 40023, toHTTPError(t, rr.Body.String()).Code)
  295. // Delete token with wrong token
  296. rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{
  297. "Authorization": util.BearerAuth("invalidtoken"),
  298. })
  299. require.Equal(t, 401, rr.Code)
  300. // Delete token with correct token
  301. rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{
  302. "Authorization": util.BearerAuth(token.Token),
  303. })
  304. require.Equal(t, 200, rr.Code)
  305. // Cannot get account anymore
  306. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  307. "Authorization": util.BearerAuth(token.Token),
  308. })
  309. require.Equal(t, 401, rr.Code)
  310. }
  311. func TestAccount_Delete_Success(t *testing.T) {
  312. conf := newTestConfigWithAuthFile(t)
  313. conf.EnableSignup = true
  314. s := newTestServer(t, conf)
  315. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  316. require.Equal(t, 200, rr.Code)
  317. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  318. "Authorization": util.BasicAuth("phil", "mypass"),
  319. })
  320. require.Equal(t, 200, rr.Code)
  321. rr = request(t, s, "DELETE", "/v1/account", `{"password":"mypass"}`, map[string]string{
  322. "Authorization": util.BasicAuth("phil", "mypass"),
  323. })
  324. require.Equal(t, 200, rr.Code)
  325. // Account was marked deleted
  326. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  327. "Authorization": util.BasicAuth("phil", "mypass"),
  328. })
  329. require.Equal(t, 401, rr.Code)
  330. // Cannot re-create account, since still exists
  331. rr = request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  332. require.Equal(t, 409, rr.Code)
  333. }
  334. func TestAccount_Delete_Not_Allowed(t *testing.T) {
  335. conf := newTestConfigWithAuthFile(t)
  336. conf.EnableSignup = true
  337. s := newTestServer(t, conf)
  338. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  339. require.Equal(t, 200, rr.Code)
  340. rr = request(t, s, "DELETE", "/v1/account", "", nil)
  341. require.Equal(t, 401, rr.Code)
  342. rr = request(t, s, "DELETE", "/v1/account", `{"password":"mypass"}`, nil)
  343. require.Equal(t, 401, rr.Code)
  344. rr = request(t, s, "DELETE", "/v1/account", `{"password":"INCORRECT"}`, map[string]string{
  345. "Authorization": util.BasicAuth("phil", "mypass"),
  346. })
  347. require.Equal(t, 400, rr.Code)
  348. require.Equal(t, 40026, toHTTPError(t, rr.Body.String()).Code)
  349. }
  350. func TestAccount_Reservation_AddWithoutTierFails(t *testing.T) {
  351. conf := newTestConfigWithAuthFile(t)
  352. conf.EnableSignup = true
  353. s := newTestServer(t, conf)
  354. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  355. require.Equal(t, 200, rr.Code)
  356. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic":"mytopic", "everyone":"deny-all"}`, map[string]string{
  357. "Authorization": util.BasicAuth("phil", "mypass"),
  358. })
  359. require.Equal(t, 401, rr.Code)
  360. }
  361. func TestAccount_Reservation_AddAdminSuccess(t *testing.T) {
  362. conf := newTestConfigWithAuthFile(t)
  363. conf.EnableSignup = true
  364. s := newTestServer(t, conf)
  365. // A user, an admin, and a reservation walk into a bar
  366. require.Nil(t, s.userManager.AddTier(&user.Tier{
  367. Code: "pro",
  368. ReservationLimit: 2,
  369. }))
  370. require.Nil(t, s.userManager.AddUser("noadmin1", "pass", user.RoleUser))
  371. require.Nil(t, s.userManager.ChangeTier("noadmin1", "pro"))
  372. require.Nil(t, s.userManager.AddReservation("noadmin1", "mytopic", user.PermissionDenyAll))
  373. require.Nil(t, s.userManager.AddUser("noadmin2", "pass", user.RoleUser))
  374. require.Nil(t, s.userManager.ChangeTier("noadmin2", "pro"))
  375. require.Nil(t, s.userManager.AddUser("phil", "adminpass", user.RoleAdmin))
  376. // Admin can reserve topic
  377. rr := request(t, s, "POST", "/v1/account/reservation", `{"topic":"sometopic","everyone":"deny-all"}`, map[string]string{
  378. "Authorization": util.BasicAuth("phil", "adminpass"),
  379. })
  380. require.Equal(t, 200, rr.Code)
  381. // User cannot reserve already reserved topic
  382. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic":"mytopic","everyone":"deny-all"}`, map[string]string{
  383. "Authorization": util.BasicAuth("noadmin2", "pass"),
  384. })
  385. require.Equal(t, 409, rr.Code)
  386. // Admin cannot reserve already reserved topic
  387. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic":"mytopic","everyone":"deny-all"}`, map[string]string{
  388. "Authorization": util.BasicAuth("phil", "adminpass"),
  389. })
  390. require.Equal(t, 409, rr.Code)
  391. reservations, err := s.userManager.Reservations("phil")
  392. require.Nil(t, err)
  393. require.Equal(t, 1, len(reservations))
  394. require.Equal(t, "sometopic", reservations[0].Topic)
  395. reservations, err = s.userManager.Reservations("noadmin1")
  396. require.Nil(t, err)
  397. require.Equal(t, 1, len(reservations))
  398. require.Equal(t, "mytopic", reservations[0].Topic)
  399. reservations, err = s.userManager.Reservations("noadmin2")
  400. require.Nil(t, err)
  401. require.Equal(t, 0, len(reservations))
  402. }
  403. func TestAccount_Reservation_AddRemoveUserWithTierSuccess(t *testing.T) {
  404. conf := newTestConfigWithAuthFile(t)
  405. conf.EnableSignup = true
  406. s := newTestServer(t, conf)
  407. // Create user
  408. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  409. require.Equal(t, 200, rr.Code)
  410. // Create a tier
  411. require.Nil(t, s.userManager.AddTier(&user.Tier{
  412. Code: "pro",
  413. MessageLimit: 123,
  414. MessageExpiryDuration: 86400 * time.Second,
  415. EmailLimit: 32,
  416. ReservationLimit: 2,
  417. AttachmentFileSizeLimit: 1231231,
  418. AttachmentTotalSizeLimit: 123123,
  419. AttachmentExpiryDuration: 10800 * time.Second,
  420. AttachmentBandwidthLimit: 21474836480,
  421. }))
  422. require.Nil(t, s.userManager.ChangeTier("phil", "pro"))
  423. // Reserve two topics
  424. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic": "mytopic", "everyone":"deny-all"}`, map[string]string{
  425. "Authorization": util.BasicAuth("phil", "mypass"),
  426. })
  427. require.Equal(t, 200, rr.Code)
  428. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic": "another", "everyone":"read-only"}`, map[string]string{
  429. "Authorization": util.BasicAuth("phil", "mypass"),
  430. })
  431. require.Equal(t, 200, rr.Code)
  432. // Trying to reserve a third should fail
  433. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic": "yet-another", "everyone":"deny-all"}`, map[string]string{
  434. "Authorization": util.BasicAuth("phil", "mypass"),
  435. })
  436. require.Equal(t, 429, rr.Code)
  437. // Modify existing should still work
  438. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic": "another", "everyone":"write-only"}`, map[string]string{
  439. "Authorization": util.BasicAuth("phil", "mypass"),
  440. })
  441. require.Equal(t, 200, rr.Code)
  442. // Check account result
  443. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  444. "Authorization": util.BasicAuth("phil", "mypass"),
  445. })
  446. require.Equal(t, 200, rr.Code)
  447. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  448. require.Equal(t, "pro", account.Tier.Code)
  449. require.Equal(t, int64(123), account.Limits.Messages)
  450. require.Equal(t, int64(86400), account.Limits.MessagesExpiryDuration)
  451. require.Equal(t, int64(32), account.Limits.Emails)
  452. require.Equal(t, int64(2), account.Limits.Reservations)
  453. require.Equal(t, int64(1231231), account.Limits.AttachmentFileSize)
  454. require.Equal(t, int64(123123), account.Limits.AttachmentTotalSize)
  455. require.Equal(t, int64(10800), account.Limits.AttachmentExpiryDuration)
  456. require.Equal(t, int64(21474836480), account.Limits.AttachmentBandwidth)
  457. require.Equal(t, 2, len(account.Reservations))
  458. require.Equal(t, "another", account.Reservations[0].Topic)
  459. require.Equal(t, "write-only", account.Reservations[0].Everyone)
  460. require.Equal(t, "mytopic", account.Reservations[1].Topic)
  461. require.Equal(t, "deny-all", account.Reservations[1].Everyone)
  462. // Delete and re-check
  463. rr = request(t, s, "DELETE", "/v1/account/reservation/another", "", map[string]string{
  464. "Authorization": util.BasicAuth("phil", "mypass"),
  465. })
  466. require.Equal(t, 200, rr.Code)
  467. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  468. "Authorization": util.BasicAuth("phil", "mypass"),
  469. })
  470. require.Equal(t, 200, rr.Code)
  471. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  472. require.Equal(t, 1, len(account.Reservations))
  473. require.Equal(t, "mytopic", account.Reservations[0].Topic)
  474. }
  475. func TestAccount_Reservation_PublishByAnonymousFails(t *testing.T) {
  476. conf := newTestConfigWithAuthFile(t)
  477. conf.AuthDefault = user.PermissionReadWrite
  478. conf.EnableSignup = true
  479. s := newTestServer(t, conf)
  480. // Create user with tier
  481. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  482. require.Equal(t, 200, rr.Code)
  483. require.Nil(t, s.userManager.AddTier(&user.Tier{
  484. Code: "pro",
  485. MessageLimit: 20,
  486. ReservationLimit: 2,
  487. }))
  488. require.Nil(t, s.userManager.ChangeTier("phil", "pro"))
  489. // Reserve a topic
  490. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic": "mytopic", "everyone":"deny-all"}`, map[string]string{
  491. "Authorization": util.BasicAuth("phil", "mypass"),
  492. })
  493. require.Equal(t, 200, rr.Code)
  494. // Publish a message
  495. rr = request(t, s, "POST", "/mytopic", `Howdy`, map[string]string{
  496. "Authorization": util.BasicAuth("phil", "mypass"),
  497. })
  498. require.Equal(t, 200, rr.Code)
  499. // Publish a message (as anonymous)
  500. rr = request(t, s, "POST", "/mytopic", `Howdy`, nil)
  501. require.Equal(t, 403, rr.Code)
  502. }
  503. func TestAccount_Reservation_Add_Kills_Other_Subscribers(t *testing.T) {
  504. conf := newTestConfigWithAuthFile(t)
  505. conf.AuthDefault = user.PermissionReadWrite
  506. conf.EnableSignup = true
  507. s := newTestServer(t, conf)
  508. defer s.closeDatabases()
  509. // Create user with tier
  510. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  511. require.Equal(t, 200, rr.Code)
  512. require.Nil(t, s.userManager.AddTier(&user.Tier{
  513. Code: "pro",
  514. MessageLimit: 20,
  515. ReservationLimit: 2,
  516. }))
  517. require.Nil(t, s.userManager.ChangeTier("phil", "pro"))
  518. // Subscribe anonymously
  519. anonCh, userCh := make(chan bool), make(chan bool)
  520. go func() {
  521. rr := request(t, s, "GET", "/mytopic/json", ``, nil) // This blocks until it's killed!
  522. require.Equal(t, 200, rr.Code)
  523. messages := toMessages(t, rr.Body.String())
  524. require.Equal(t, 2, len(messages)) // This is the meat. We should NOT receive the second message!
  525. require.Equal(t, "open", messages[0].Event)
  526. require.Equal(t, "message before reservation", messages[1].Message)
  527. anonCh <- true
  528. log.Info("Anonymous subscription ended")
  529. }()
  530. // Subscribe with user
  531. go func() {
  532. rr := request(t, s, "GET", "/mytopic/json", ``, map[string]string{ // Blocks!
  533. "Authorization": util.BasicAuth("phil", "mypass"),
  534. })
  535. require.Equal(t, 200, rr.Code)
  536. messages := toMessages(t, rr.Body.String())
  537. require.Equal(t, 3, len(messages))
  538. require.Equal(t, "open", messages[0].Event)
  539. require.Equal(t, "message before reservation", messages[1].Message)
  540. require.Equal(t, "message after reservation", messages[2].Message)
  541. userCh <- true
  542. log.Info("User subscription ended")
  543. }()
  544. // Publish message (before reservation)
  545. time.Sleep(2 * time.Second) // Wait for subscribers
  546. rr = request(t, s, "POST", "/mytopic", "message before reservation", nil)
  547. require.Equal(t, 200, rr.Code)
  548. time.Sleep(2 * time.Second) // Wait for subscribers to receive message
  549. // Reserve a topic
  550. rr = request(t, s, "POST", "/v1/account/reservation", `{"topic": "mytopic", "everyone":"deny-all"}`, map[string]string{
  551. "Authorization": util.BasicAuth("phil", "mypass"),
  552. })
  553. require.Equal(t, 200, rr.Code)
  554. // Everyone but phil should be killed
  555. select {
  556. case <-anonCh:
  557. case <-time.After(5 * time.Second):
  558. t.Fatal("Waiting for anonymous subscription to be killed failed")
  559. }
  560. // Publish a message
  561. rr = request(t, s, "POST", "/mytopic", "message after reservation", map[string]string{
  562. "Authorization": util.BasicAuth("phil", "mypass"),
  563. })
  564. require.Equal(t, 200, rr.Code)
  565. // Kill user Go routine
  566. s.topics["mytopic"].CancelSubscribers("<invalid>")
  567. select {
  568. case <-userCh:
  569. case <-time.After(5 * time.Second):
  570. t.Fatal("Waiting for user subscription to be killed failed")
  571. }
  572. }
  573. func TestAccount_Persist_UserStats_After_Tier_Change(t *testing.T) {
  574. conf := newTestConfigWithAuthFile(t)
  575. conf.AuthDefault = user.PermissionReadWrite
  576. conf.AuthStatsQueueWriterInterval = 200 * time.Millisecond
  577. s := newTestServer(t, conf)
  578. defer s.closeDatabases()
  579. // Create user with tier
  580. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  581. require.Nil(t, s.userManager.AddTier(&user.Tier{
  582. Code: "starter",
  583. MessageLimit: 10,
  584. }))
  585. require.Nil(t, s.userManager.AddTier(&user.Tier{
  586. Code: "pro",
  587. MessageLimit: 20,
  588. }))
  589. require.Nil(t, s.userManager.ChangeTier("phil", "starter"))
  590. // Publish a message
  591. rr := request(t, s, "POST", "/mytopic", "hi", map[string]string{
  592. "Authorization": util.BasicAuth("phil", "phil"),
  593. })
  594. require.Equal(t, 200, rr.Code)
  595. // Wait for stats queue writer
  596. time.Sleep(300 * time.Millisecond)
  597. // Verify that message stats were persisted
  598. u, err := s.userManager.User("phil")
  599. require.Nil(t, err)
  600. require.Equal(t, int64(1), u.Stats.Messages)
  601. // Change tier, make a request (to reset limiters)
  602. require.Nil(t, s.userManager.ChangeTier("phil", "pro"))
  603. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  604. "Authorization": util.BasicAuth("phil", "phil"),
  605. })
  606. require.Equal(t, 200, rr.Code)
  607. // Verify that message stats were persisted
  608. time.Sleep(300 * time.Millisecond)
  609. u, err = s.userManager.User("phil")
  610. require.Nil(t, err)
  611. require.Equal(t, int64(0), u.Stats.Messages) // v.EnqueueUserStats had run!
  612. }