auth_sqlite_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package auth_test
  2. import (
  3. "github.com/stretchr/testify/require"
  4. "heckel.io/ntfy/auth"
  5. "path/filepath"
  6. "strings"
  7. "testing"
  8. "time"
  9. )
  10. func TestSQLiteAuth_FullScenario_Default_DenyAll(t *testing.T) {
  11. a := newTestAuth(t, false, false)
  12. require.Nil(t, a.AddUser("phil", "phil", auth.RoleAdmin))
  13. require.Nil(t, a.AddUser("ben", "ben", auth.RoleUser))
  14. require.Nil(t, a.AllowAccess("ben", "mytopic", true, true))
  15. require.Nil(t, a.AllowAccess("ben", "readme", true, false))
  16. require.Nil(t, a.AllowAccess("ben", "writeme", false, true))
  17. require.Nil(t, a.AllowAccess("ben", "everyonewrite", false, false)) // How unfair!
  18. require.Nil(t, a.AllowAccess(auth.Everyone, "announcements", true, false))
  19. require.Nil(t, a.AllowAccess(auth.Everyone, "everyonewrite", true, true))
  20. require.Nil(t, a.AllowAccess(auth.Everyone, "up*", false, true)) // Everyone can write to /up*
  21. phil, err := a.Authenticate("phil", "phil")
  22. require.Nil(t, err)
  23. require.Equal(t, "phil", phil.Name)
  24. require.True(t, strings.HasPrefix(phil.Hash, "$2a$11$"))
  25. require.Equal(t, auth.RoleAdmin, phil.Role)
  26. require.Equal(t, []auth.Grant{}, phil.Grants)
  27. ben, err := a.Authenticate("ben", "ben")
  28. require.Nil(t, err)
  29. require.Equal(t, "ben", ben.Name)
  30. require.True(t, strings.HasPrefix(ben.Hash, "$2a$11$"))
  31. require.Equal(t, auth.RoleUser, ben.Role)
  32. require.Equal(t, []auth.Grant{
  33. {"mytopic", true, true},
  34. {"readme", true, false},
  35. {"writeme", false, true},
  36. {"everyonewrite", false, false},
  37. }, ben.Grants)
  38. notben, err := a.Authenticate("ben", "this is wrong")
  39. require.Nil(t, notben)
  40. require.Equal(t, auth.ErrUnauthenticated, err)
  41. // Admin can do everything
  42. require.Nil(t, a.Authorize(phil, "sometopic", auth.PermissionWrite))
  43. require.Nil(t, a.Authorize(phil, "mytopic", auth.PermissionRead))
  44. require.Nil(t, a.Authorize(phil, "readme", auth.PermissionWrite))
  45. require.Nil(t, a.Authorize(phil, "writeme", auth.PermissionWrite))
  46. require.Nil(t, a.Authorize(phil, "announcements", auth.PermissionWrite))
  47. require.Nil(t, a.Authorize(phil, "everyonewrite", auth.PermissionWrite))
  48. // User cannot do everything
  49. require.Nil(t, a.Authorize(ben, "mytopic", auth.PermissionWrite))
  50. require.Nil(t, a.Authorize(ben, "mytopic", auth.PermissionRead))
  51. require.Nil(t, a.Authorize(ben, "readme", auth.PermissionRead))
  52. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "readme", auth.PermissionWrite))
  53. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "writeme", auth.PermissionRead))
  54. require.Nil(t, a.Authorize(ben, "writeme", auth.PermissionWrite))
  55. require.Nil(t, a.Authorize(ben, "writeme", auth.PermissionWrite))
  56. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "everyonewrite", auth.PermissionRead))
  57. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "everyonewrite", auth.PermissionWrite))
  58. require.Nil(t, a.Authorize(ben, "announcements", auth.PermissionRead))
  59. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "announcements", auth.PermissionWrite))
  60. // Everyone else can do barely anything
  61. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", auth.PermissionRead))
  62. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", auth.PermissionWrite))
  63. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "mytopic", auth.PermissionRead))
  64. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "mytopic", auth.PermissionWrite))
  65. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "readme", auth.PermissionRead))
  66. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "readme", auth.PermissionWrite))
  67. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "writeme", auth.PermissionRead))
  68. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "writeme", auth.PermissionWrite))
  69. require.Equal(t, auth.ErrUnauthorized, a.Authorize(nil, "announcements", auth.PermissionWrite))
  70. require.Nil(t, a.Authorize(nil, "announcements", auth.PermissionRead))
  71. require.Nil(t, a.Authorize(nil, "everyonewrite", auth.PermissionRead))
  72. require.Nil(t, a.Authorize(nil, "everyonewrite", auth.PermissionWrite))
  73. require.Nil(t, a.Authorize(nil, "up1234", auth.PermissionWrite)) // Wildcard permission
  74. require.Nil(t, a.Authorize(nil, "up5678", auth.PermissionWrite))
  75. }
  76. func TestSQLiteAuth_AddUser_Invalid(t *testing.T) {
  77. a := newTestAuth(t, false, false)
  78. require.Equal(t, auth.ErrInvalidArgument, a.AddUser(" invalid ", "pass", auth.RoleAdmin))
  79. require.Equal(t, auth.ErrInvalidArgument, a.AddUser("validuser", "pass", "invalid-role"))
  80. }
  81. func TestSQLiteAuth_AddUser_Timing(t *testing.T) {
  82. a := newTestAuth(t, false, false)
  83. start := time.Now().UnixMilli()
  84. require.Nil(t, a.AddUser("user", "pass", auth.RoleAdmin))
  85. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
  86. }
  87. func TestSQLiteAuth_Authenticate_Timing(t *testing.T) {
  88. a := newTestAuth(t, false, false)
  89. require.Nil(t, a.AddUser("user", "pass", auth.RoleAdmin))
  90. // Timing a correct attempt
  91. start := time.Now().UnixMilli()
  92. _, err := a.Authenticate("user", "pass")
  93. require.Nil(t, err)
  94. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
  95. // Timing an incorrect attempt
  96. start = time.Now().UnixMilli()
  97. _, err = a.Authenticate("user", "INCORRECT")
  98. require.Equal(t, auth.ErrUnauthenticated, err)
  99. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
  100. // Timing a non-existing user attempt
  101. start = time.Now().UnixMilli()
  102. _, err = a.Authenticate("DOES-NOT-EXIST", "hithere")
  103. require.Equal(t, auth.ErrUnauthenticated, err)
  104. require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
  105. }
  106. func TestSQLiteAuth_UserManagement(t *testing.T) {
  107. a := newTestAuth(t, false, false)
  108. require.Nil(t, a.AddUser("phil", "phil", auth.RoleAdmin))
  109. require.Nil(t, a.AddUser("ben", "ben", auth.RoleUser))
  110. require.Nil(t, a.AllowAccess("ben", "mytopic", true, true))
  111. require.Nil(t, a.AllowAccess("ben", "readme", true, false))
  112. require.Nil(t, a.AllowAccess("ben", "writeme", false, true))
  113. require.Nil(t, a.AllowAccess("ben", "everyonewrite", false, false)) // How unfair!
  114. require.Nil(t, a.AllowAccess(auth.Everyone, "announcements", true, false))
  115. require.Nil(t, a.AllowAccess(auth.Everyone, "everyonewrite", true, true))
  116. // Query user details
  117. phil, err := a.User("phil")
  118. require.Nil(t, err)
  119. require.Equal(t, "phil", phil.Name)
  120. require.True(t, strings.HasPrefix(phil.Hash, "$2a$11$"))
  121. require.Equal(t, auth.RoleAdmin, phil.Role)
  122. require.Equal(t, []auth.Grant{}, phil.Grants)
  123. ben, err := a.User("ben")
  124. require.Nil(t, err)
  125. require.Equal(t, "ben", ben.Name)
  126. require.True(t, strings.HasPrefix(ben.Hash, "$2a$11$"))
  127. require.Equal(t, auth.RoleUser, ben.Role)
  128. require.Equal(t, []auth.Grant{
  129. {"mytopic", true, true},
  130. {"readme", true, false},
  131. {"writeme", false, true},
  132. {"everyonewrite", false, false},
  133. }, ben.Grants)
  134. everyone, err := a.User(auth.Everyone)
  135. require.Nil(t, err)
  136. require.Equal(t, "*", everyone.Name)
  137. require.Equal(t, "", everyone.Hash)
  138. require.Equal(t, auth.RoleAnonymous, everyone.Role)
  139. require.Equal(t, []auth.Grant{
  140. {"announcements", true, false},
  141. {"everyonewrite", true, true},
  142. }, everyone.Grants)
  143. // Ben: Before revoking
  144. require.Nil(t, a.AllowAccess("ben", "mytopic", true, true))
  145. require.Nil(t, a.AllowAccess("ben", "readme", true, false))
  146. require.Nil(t, a.AllowAccess("ben", "writeme", false, true))
  147. require.Nil(t, a.Authorize(ben, "mytopic", auth.PermissionRead))
  148. require.Nil(t, a.Authorize(ben, "mytopic", auth.PermissionWrite))
  149. require.Nil(t, a.Authorize(ben, "readme", auth.PermissionRead))
  150. require.Nil(t, a.Authorize(ben, "writeme", auth.PermissionWrite))
  151. // Revoke access for "ben" to "mytopic", then check again
  152. require.Nil(t, a.ResetAccess("ben", "mytopic"))
  153. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "mytopic", auth.PermissionWrite)) // Revoked
  154. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "mytopic", auth.PermissionRead)) // Revoked
  155. require.Nil(t, a.Authorize(ben, "readme", auth.PermissionRead)) // Unchanged
  156. require.Nil(t, a.Authorize(ben, "writeme", auth.PermissionWrite)) // Unchanged
  157. // Revoke rest of the access
  158. require.Nil(t, a.ResetAccess("ben", ""))
  159. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "readme", auth.PermissionRead)) // Revoked
  160. require.Equal(t, auth.ErrUnauthorized, a.Authorize(ben, "wrtiteme", auth.PermissionWrite)) // Revoked
  161. // User list
  162. users, err := a.Users()
  163. require.Nil(t, err)
  164. require.Equal(t, 3, len(users))
  165. require.Equal(t, "phil", users[0].Name)
  166. require.Equal(t, "ben", users[1].Name)
  167. require.Equal(t, "*", users[2].Name)
  168. // Remove user
  169. require.Nil(t, a.RemoveUser("ben"))
  170. _, err = a.User("ben")
  171. require.Equal(t, auth.ErrNotFound, err)
  172. users, err = a.Users()
  173. require.Nil(t, err)
  174. require.Equal(t, 2, len(users))
  175. require.Equal(t, "phil", users[0].Name)
  176. require.Equal(t, "*", users[1].Name)
  177. }
  178. func TestSQLiteAuth_ChangePassword(t *testing.T) {
  179. a := newTestAuth(t, false, false)
  180. require.Nil(t, a.AddUser("phil", "phil", auth.RoleAdmin))
  181. _, err := a.Authenticate("phil", "phil")
  182. require.Nil(t, err)
  183. require.Nil(t, a.ChangePassword("phil", "newpass"))
  184. _, err = a.Authenticate("phil", "phil")
  185. require.Equal(t, auth.ErrUnauthenticated, err)
  186. _, err = a.Authenticate("phil", "newpass")
  187. require.Nil(t, err)
  188. }
  189. func TestSQLiteAuth_ChangeRole(t *testing.T) {
  190. a := newTestAuth(t, false, false)
  191. require.Nil(t, a.AddUser("ben", "ben", auth.RoleUser))
  192. require.Nil(t, a.AllowAccess("ben", "mytopic", true, true))
  193. require.Nil(t, a.AllowAccess("ben", "readme", true, false))
  194. ben, err := a.User("ben")
  195. require.Nil(t, err)
  196. require.Equal(t, auth.RoleUser, ben.Role)
  197. require.Equal(t, 2, len(ben.Grants))
  198. require.Nil(t, a.ChangeRole("ben", auth.RoleAdmin))
  199. ben, err = a.User("ben")
  200. require.Nil(t, err)
  201. require.Equal(t, auth.RoleAdmin, ben.Role)
  202. require.Equal(t, 0, len(ben.Grants))
  203. }
  204. func newTestAuth(t *testing.T, defaultRead, defaultWrite bool) *auth.SQLiteAuth {
  205. filename := filepath.Join(t.TempDir(), "user.db")
  206. a, err := auth.NewSQLiteAuth(filename, defaultRead, defaultWrite)
  207. require.Nil(t, err)
  208. return a
  209. }