server_admin_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. package server
  2. import (
  3. "github.com/stretchr/testify/require"
  4. "heckel.io/ntfy/v2/user"
  5. "heckel.io/ntfy/v2/util"
  6. "sync/atomic"
  7. "testing"
  8. "time"
  9. )
  10. func TestUser_AddRemove(t *testing.T) {
  11. s := newTestServer(t, newTestConfigWithAuthFile(t))
  12. defer s.closeDatabases()
  13. // Create admin, tier
  14. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  15. require.Nil(t, s.userManager.AddTier(&user.Tier{
  16. Code: "tier1",
  17. }))
  18. // Create user via API
  19. rr := request(t, s, "POST", "/v1/users", `{"username": "ben", "password":"ben"}`, map[string]string{
  20. "Authorization": util.BasicAuth("phil", "phil"),
  21. })
  22. require.Equal(t, 200, rr.Code)
  23. // Create user with tier via API
  24. rr = request(t, s, "PUT", "/v1/users", `{"username": "emma", "password":"emma", "tier": "tier1"}`, map[string]string{
  25. "Authorization": util.BasicAuth("phil", "phil"),
  26. })
  27. require.Equal(t, 200, rr.Code)
  28. // Check users
  29. users, err := s.userManager.Users()
  30. require.Nil(t, err)
  31. require.Equal(t, 4, len(users))
  32. require.Equal(t, "phil", users[0].Name)
  33. require.Equal(t, "ben", users[1].Name)
  34. require.Equal(t, user.RoleUser, users[1].Role)
  35. require.Nil(t, users[1].Tier)
  36. require.Equal(t, "emma", users[2].Name)
  37. require.Equal(t, user.RoleUser, users[2].Role)
  38. require.Equal(t, "tier1", users[2].Tier.Code)
  39. require.Equal(t, user.Everyone, users[3].Name)
  40. // Delete user via API
  41. rr = request(t, s, "DELETE", "/v1/users", `{"username": "ben"}`, map[string]string{
  42. "Authorization": util.BasicAuth("phil", "phil"),
  43. })
  44. require.Equal(t, 200, rr.Code)
  45. // Check user was deleted
  46. users, err = s.userManager.Users()
  47. require.Nil(t, err)
  48. require.Equal(t, 3, len(users))
  49. require.Equal(t, "phil", users[0].Name)
  50. require.Equal(t, "emma", users[1].Name)
  51. require.Equal(t, user.Everyone, users[2].Name)
  52. // Reject invalid user change
  53. rr = request(t, s, "PUT", "/v1/users", `{"username": "ben"}`, map[string]string{
  54. "Authorization": util.BasicAuth("phil", "phil"),
  55. })
  56. require.Equal(t, 400, rr.Code)
  57. }
  58. func TestUser_AddWithPasswordHash(t *testing.T) {
  59. s := newTestServer(t, newTestConfigWithAuthFile(t))
  60. defer s.closeDatabases()
  61. // Create admin
  62. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  63. // Create user via API
  64. rr := request(t, s, "POST", "/v1/users", `{"username": "ben", "hash":"$2a$04$2aPIIqPXQU16OfkSUZH1XOzpu1gsPRKkrfVdFLgWQ.tqb.vtTCuVe"}`, map[string]string{
  65. "Authorization": util.BasicAuth("phil", "phil"),
  66. })
  67. require.Equal(t, 200, rr.Code)
  68. // Check that user can login with password
  69. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  70. "Authorization": util.BasicAuth("ben", "ben"),
  71. })
  72. require.Equal(t, 200, rr.Code)
  73. // Check users
  74. users, err := s.userManager.Users()
  75. require.Nil(t, err)
  76. require.Equal(t, 3, len(users))
  77. require.Equal(t, "phil", users[0].Name)
  78. require.Equal(t, user.RoleAdmin, users[0].Role)
  79. require.Equal(t, "ben", users[1].Name)
  80. require.Equal(t, user.RoleUser, users[1].Role)
  81. }
  82. func TestUser_ChangeUserPassword(t *testing.T) {
  83. s := newTestServer(t, newTestConfigWithAuthFile(t))
  84. defer s.closeDatabases()
  85. // Create admin
  86. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  87. // Create user via API
  88. rr := request(t, s, "POST", "/v1/users", `{"username": "ben", "password": "ben"}`, map[string]string{
  89. "Authorization": util.BasicAuth("phil", "phil"),
  90. })
  91. require.Equal(t, 200, rr.Code)
  92. // Try to login with first password
  93. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  94. "Authorization": util.BasicAuth("ben", "ben"),
  95. })
  96. require.Equal(t, 200, rr.Code)
  97. // Change password via API
  98. rr = request(t, s, "PUT", "/v1/users", `{"username": "ben", "password": "ben-two"}`, map[string]string{
  99. "Authorization": util.BasicAuth("phil", "phil"),
  100. })
  101. require.Equal(t, 200, rr.Code)
  102. // Make sure first password fails
  103. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  104. "Authorization": util.BasicAuth("ben", "ben"),
  105. })
  106. require.Equal(t, 401, rr.Code)
  107. // Try to login with second password
  108. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  109. "Authorization": util.BasicAuth("ben", "ben-two"),
  110. })
  111. require.Equal(t, 200, rr.Code)
  112. }
  113. func TestUser_ChangeUserTier(t *testing.T) {
  114. s := newTestServer(t, newTestConfigWithAuthFile(t))
  115. defer s.closeDatabases()
  116. // Create admin, tier
  117. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  118. require.Nil(t, s.userManager.AddTier(&user.Tier{
  119. Code: "tier1",
  120. }))
  121. require.Nil(t, s.userManager.AddTier(&user.Tier{
  122. Code: "tier2",
  123. }))
  124. // Create user with tier via API
  125. rr := request(t, s, "POST", "/v1/users", `{"username": "ben", "password":"ben", "tier": "tier1"}`, map[string]string{
  126. "Authorization": util.BasicAuth("phil", "phil"),
  127. })
  128. require.Equal(t, 200, rr.Code)
  129. // Check users
  130. users, err := s.userManager.Users()
  131. require.Nil(t, err)
  132. require.Equal(t, 3, len(users))
  133. require.Equal(t, "phil", users[0].Name)
  134. require.Equal(t, "ben", users[1].Name)
  135. require.Equal(t, user.RoleUser, users[1].Role)
  136. require.Equal(t, "tier1", users[1].Tier.Code)
  137. // Change user tier via API
  138. rr = request(t, s, "PUT", "/v1/users", `{"username": "ben", "tier": "tier2"}`, map[string]string{
  139. "Authorization": util.BasicAuth("phil", "phil"),
  140. })
  141. require.Equal(t, 200, rr.Code)
  142. // Check users again
  143. users, err = s.userManager.Users()
  144. require.Nil(t, err)
  145. require.Equal(t, "tier2", users[1].Tier.Code)
  146. }
  147. func TestUser_ChangeUserPasswordAndTier(t *testing.T) {
  148. s := newTestServer(t, newTestConfigWithAuthFile(t))
  149. defer s.closeDatabases()
  150. // Create admin, tier
  151. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  152. require.Nil(t, s.userManager.AddTier(&user.Tier{
  153. Code: "tier1",
  154. }))
  155. require.Nil(t, s.userManager.AddTier(&user.Tier{
  156. Code: "tier2",
  157. }))
  158. // Create user with tier via API
  159. rr := request(t, s, "POST", "/v1/users", `{"username": "ben", "password":"ben", "tier": "tier1"}`, map[string]string{
  160. "Authorization": util.BasicAuth("phil", "phil"),
  161. })
  162. require.Equal(t, 200, rr.Code)
  163. // Check users
  164. users, err := s.userManager.Users()
  165. require.Nil(t, err)
  166. require.Equal(t, 3, len(users))
  167. require.Equal(t, "phil", users[0].Name)
  168. require.Equal(t, "ben", users[1].Name)
  169. require.Equal(t, user.RoleUser, users[1].Role)
  170. require.Equal(t, "tier1", users[1].Tier.Code)
  171. // Change user password and tier via API
  172. rr = request(t, s, "PUT", "/v1/users", `{"username": "ben", "password":"ben-two", "tier": "tier2"}`, map[string]string{
  173. "Authorization": util.BasicAuth("phil", "phil"),
  174. })
  175. require.Equal(t, 200, rr.Code)
  176. // Make sure first password fails
  177. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  178. "Authorization": util.BasicAuth("ben", "ben"),
  179. })
  180. require.Equal(t, 401, rr.Code)
  181. // Try to login with second password
  182. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  183. "Authorization": util.BasicAuth("ben", "ben-two"),
  184. })
  185. require.Equal(t, 200, rr.Code)
  186. // Check new tier
  187. users, err = s.userManager.Users()
  188. require.Nil(t, err)
  189. require.Equal(t, "tier2", users[1].Tier.Code)
  190. }
  191. func TestUser_ChangeUserPasswordWithHash(t *testing.T) {
  192. s := newTestServer(t, newTestConfigWithAuthFile(t))
  193. defer s.closeDatabases()
  194. // Create admin
  195. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  196. // Create user with tier via API
  197. rr := request(t, s, "POST", "/v1/users", `{"username": "ben", "password":"not-ben"}`, map[string]string{
  198. "Authorization": util.BasicAuth("phil", "phil"),
  199. })
  200. require.Equal(t, 200, rr.Code)
  201. // Try to login with first password
  202. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  203. "Authorization": util.BasicAuth("ben", "not-ben"),
  204. })
  205. require.Equal(t, 200, rr.Code)
  206. // Change user password and tier via API
  207. rr = request(t, s, "PUT", "/v1/users", `{"username": "ben", "hash":"$2a$04$2aPIIqPXQU16OfkSUZH1XOzpu1gsPRKkrfVdFLgWQ.tqb.vtTCuVe"}`, map[string]string{
  208. "Authorization": util.BasicAuth("phil", "phil"),
  209. })
  210. require.Equal(t, 200, rr.Code)
  211. // Try to login with second password
  212. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  213. "Authorization": util.BasicAuth("ben", "ben"),
  214. })
  215. require.Equal(t, 200, rr.Code)
  216. }
  217. func TestUser_DontChangeAdminPassword(t *testing.T) {
  218. s := newTestServer(t, newTestConfigWithAuthFile(t))
  219. defer s.closeDatabases()
  220. // Create admin
  221. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  222. require.Nil(t, s.userManager.AddUser("admin", "admin", user.RoleAdmin, false))
  223. // Try to change password via API
  224. rr := request(t, s, "PUT", "/v1/users", `{"username": "admin", "password": "admin-new"}`, map[string]string{
  225. "Authorization": util.BasicAuth("phil", "phil"),
  226. })
  227. require.Equal(t, 403, rr.Code)
  228. }
  229. func TestUser_AddRemove_Failures(t *testing.T) {
  230. s := newTestServer(t, newTestConfigWithAuthFile(t))
  231. defer s.closeDatabases()
  232. // Create admin
  233. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  234. require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser, false))
  235. // Cannot create user with invalid username
  236. rr := request(t, s, "POST", "/v1/users", `{"username": "not valid", "password":"ben"}`, map[string]string{
  237. "Authorization": util.BasicAuth("phil", "phil"),
  238. })
  239. require.Equal(t, 400, rr.Code)
  240. // Cannot create user if user already exists
  241. rr = request(t, s, "POST", "/v1/users", `{"username": "phil", "password":"phil"}`, map[string]string{
  242. "Authorization": util.BasicAuth("phil", "phil"),
  243. })
  244. require.Equal(t, 40901, toHTTPError(t, rr.Body.String()).Code)
  245. // Cannot create user with invalid tier
  246. rr = request(t, s, "POST", "/v1/users", `{"username": "emma", "password":"emma", "tier": "invalid"}`, map[string]string{
  247. "Authorization": util.BasicAuth("phil", "phil"),
  248. })
  249. require.Equal(t, 40030, toHTTPError(t, rr.Body.String()).Code)
  250. // Cannot delete user as non-admin
  251. rr = request(t, s, "DELETE", "/v1/users", `{"username": "ben"}`, map[string]string{
  252. "Authorization": util.BasicAuth("ben", "ben"),
  253. })
  254. require.Equal(t, 401, rr.Code)
  255. // Delete user via API
  256. rr = request(t, s, "DELETE", "/v1/users", `{"username": "ben"}`, map[string]string{
  257. "Authorization": util.BasicAuth("phil", "phil"),
  258. })
  259. require.Equal(t, 200, rr.Code)
  260. }
  261. func TestAccess_AllowReset(t *testing.T) {
  262. c := newTestConfigWithAuthFile(t)
  263. c.AuthDefault = user.PermissionDenyAll
  264. s := newTestServer(t, c)
  265. defer s.closeDatabases()
  266. // User and admin
  267. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  268. require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser, false))
  269. // Subscribing not allowed
  270. rr := request(t, s, "GET", "/gold/json?poll=1", "", map[string]string{
  271. "Authorization": util.BasicAuth("ben", "ben"),
  272. })
  273. require.Equal(t, 403, rr.Code)
  274. // Grant access
  275. rr = request(t, s, "POST", "/v1/users/access", `{"username": "ben", "topic":"gold", "permission":"ro"}`, map[string]string{
  276. "Authorization": util.BasicAuth("phil", "phil"),
  277. })
  278. require.Equal(t, 200, rr.Code)
  279. // Now subscribing is allowed
  280. rr = request(t, s, "GET", "/gold/json?poll=1", "", map[string]string{
  281. "Authorization": util.BasicAuth("ben", "ben"),
  282. })
  283. require.Equal(t, 200, rr.Code)
  284. // Reset access
  285. rr = request(t, s, "DELETE", "/v1/users/access", `{"username": "ben", "topic":"gold"}`, map[string]string{
  286. "Authorization": util.BasicAuth("phil", "phil"),
  287. })
  288. require.Equal(t, 200, rr.Code)
  289. // Subscribing not allowed (again)
  290. rr = request(t, s, "GET", "/gold/json?poll=1", "", map[string]string{
  291. "Authorization": util.BasicAuth("ben", "ben"),
  292. })
  293. require.Equal(t, 403, rr.Code)
  294. }
  295. func TestAccess_AllowReset_NonAdminAttempt(t *testing.T) {
  296. c := newTestConfigWithAuthFile(t)
  297. c.AuthDefault = user.PermissionDenyAll
  298. s := newTestServer(t, c)
  299. defer s.closeDatabases()
  300. // User
  301. require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser, false))
  302. // Grant access fails, because non-admin
  303. rr := request(t, s, "POST", "/v1/users/access", `{"username": "ben", "topic":"gold", "permission":"ro"}`, map[string]string{
  304. "Authorization": util.BasicAuth("ben", "ben"),
  305. })
  306. require.Equal(t, 401, rr.Code)
  307. }
  308. func TestAccess_AllowReset_KillConnection(t *testing.T) {
  309. c := newTestConfigWithAuthFile(t)
  310. c.AuthDefault = user.PermissionDenyAll
  311. s := newTestServer(t, c)
  312. defer s.closeDatabases()
  313. // User and admin, grant access to "gol*" topics
  314. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin, false))
  315. require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser, false))
  316. require.Nil(t, s.userManager.AllowAccess("ben", "gol*", user.PermissionRead)) // Wildcard!
  317. start, timeTaken := time.Now(), atomic.Int64{}
  318. go func() {
  319. rr := request(t, s, "GET", "/gold/json", "", map[string]string{
  320. "Authorization": util.BasicAuth("ben", "ben"),
  321. })
  322. require.Equal(t, 200, rr.Code)
  323. timeTaken.Store(time.Since(start).Milliseconds())
  324. }()
  325. time.Sleep(500 * time.Millisecond)
  326. // Reset access
  327. rr := request(t, s, "DELETE", "/v1/users/access", `{"username": "ben", "topic":"gol*"}`, map[string]string{
  328. "Authorization": util.BasicAuth("phil", "phil"),
  329. })
  330. require.Equal(t, 200, rr.Code)
  331. // Wait for connection to be killed; this will fail if the connection is never killed
  332. waitFor(t, func() bool {
  333. return timeTaken.Load() >= 500
  334. })
  335. }