server_account_test.go 22 KB

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