server_account_test.go 24 KB

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