123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- package user
- import (
- "database/sql"
- "github.com/stretchr/testify/require"
- "path/filepath"
- "strings"
- "testing"
- "time"
- )
- const minBcryptTimingMillis = int64(50) // Ideally should be >100ms, but this should also run on a Raspberry Pi without massive resources
- func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, "unit-test"))
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
- require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
- require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
- require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
- require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
- require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
- require.Nil(t, a.AllowAccess(Everyone, "up*", PermissionWrite)) // Everyone can write to /up*
- phil, err := a.Authenticate("phil", "phil")
- require.Nil(t, err)
- require.Equal(t, "phil", phil.Name)
- require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
- require.Equal(t, RoleAdmin, phil.Role)
- philGrants, err := a.Grants("phil")
- require.Nil(t, err)
- require.Equal(t, []Grant{}, philGrants)
- ben, err := a.Authenticate("ben", "ben")
- require.Nil(t, err)
- require.Equal(t, "ben", ben.Name)
- require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
- require.Equal(t, RoleUser, ben.Role)
- benGrants, err := a.Grants("ben")
- require.Nil(t, err)
- require.Equal(t, []Grant{
- {"mytopic", PermissionReadWrite},
- {"writeme", PermissionWrite},
- {"readme", PermissionRead},
- {"everyonewrite", PermissionDenyAll},
- }, benGrants)
- notben, err := a.Authenticate("ben", "this is wrong")
- require.Nil(t, notben)
- require.Equal(t, ErrUnauthenticated, err)
- // Admin can do everything
- require.Nil(t, a.Authorize(phil, "sometopic", PermissionWrite))
- require.Nil(t, a.Authorize(phil, "mytopic", PermissionRead))
- require.Nil(t, a.Authorize(phil, "readme", PermissionWrite))
- require.Nil(t, a.Authorize(phil, "writeme", PermissionWrite))
- require.Nil(t, a.Authorize(phil, "announcements", PermissionWrite))
- require.Nil(t, a.Authorize(phil, "everyonewrite", PermissionWrite))
- // User cannot do everything
- require.Nil(t, a.Authorize(ben, "mytopic", PermissionWrite))
- require.Nil(t, a.Authorize(ben, "mytopic", PermissionRead))
- require.Nil(t, a.Authorize(ben, "readme", PermissionRead))
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "readme", PermissionWrite))
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "writeme", PermissionRead))
- require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
- require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "everyonewrite", PermissionRead))
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "everyonewrite", PermissionWrite))
- require.Nil(t, a.Authorize(ben, "announcements", PermissionRead))
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "announcements", PermissionWrite))
- // Everyone else can do barely anything
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionRead))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionWrite))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopic", PermissionRead))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopic", PermissionWrite))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "readme", PermissionRead))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "readme", PermissionWrite))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "writeme", PermissionRead))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "writeme", PermissionWrite))
- require.Equal(t, ErrUnauthorized, a.Authorize(nil, "announcements", PermissionWrite))
- require.Nil(t, a.Authorize(nil, "announcements", PermissionRead))
- require.Nil(t, a.Authorize(nil, "everyonewrite", PermissionRead))
- require.Nil(t, a.Authorize(nil, "everyonewrite", PermissionWrite))
- require.Nil(t, a.Authorize(nil, "up1234", PermissionWrite)) // Wildcard permission
- require.Nil(t, a.Authorize(nil, "up5678", PermissionWrite))
- }
- func TestManager_AddUser_Invalid(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Equal(t, ErrInvalidArgument, a.AddUser(" invalid ", "pass", RoleAdmin, "unit-test"))
- require.Equal(t, ErrInvalidArgument, a.AddUser("validuser", "pass", "invalid-role", "unit-test"))
- }
- func TestManager_AddUser_Timing(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- start := time.Now().UnixMilli()
- require.Nil(t, a.AddUser("user", "pass", RoleAdmin, "unit-test"))
- require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
- }
- func TestManager_Authenticate_Timing(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("user", "pass", RoleAdmin, "unit-test"))
- // Timing a correct attempt
- start := time.Now().UnixMilli()
- _, err := a.Authenticate("user", "pass")
- require.Nil(t, err)
- require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
- // Timing an incorrect attempt
- start = time.Now().UnixMilli()
- _, err = a.Authenticate("user", "INCORRECT")
- require.Equal(t, ErrUnauthenticated, err)
- require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
- // Timing a non-existing user attempt
- start = time.Now().UnixMilli()
- _, err = a.Authenticate("DOES-NOT-EXIST", "hithere")
- require.Equal(t, ErrUnauthenticated, err)
- require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
- }
- func TestManager_UserManagement(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, "unit-test"))
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
- require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
- require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
- require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
- require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
- require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
- // Query user details
- phil, err := a.User("phil")
- require.Nil(t, err)
- require.Equal(t, "phil", phil.Name)
- require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
- require.Equal(t, RoleAdmin, phil.Role)
- philGrants, err := a.Grants("phil")
- require.Nil(t, err)
- require.Equal(t, []Grant{}, philGrants)
- ben, err := a.User("ben")
- require.Nil(t, err)
- require.Equal(t, "ben", ben.Name)
- require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
- require.Equal(t, RoleUser, ben.Role)
- benGrants, err := a.Grants("ben")
- require.Nil(t, err)
- require.Equal(t, []Grant{
- {"mytopic", PermissionReadWrite},
- {"writeme", PermissionWrite},
- {"readme", PermissionRead},
- {"everyonewrite", PermissionDenyAll},
- }, benGrants)
- everyone, err := a.User(Everyone)
- require.Nil(t, err)
- require.Equal(t, "*", everyone.Name)
- require.Equal(t, "", everyone.Hash)
- require.Equal(t, RoleAnonymous, everyone.Role)
- everyoneGrants, err := a.Grants(Everyone)
- require.Nil(t, err)
- require.Equal(t, []Grant{
- {"everyonewrite", PermissionReadWrite},
- {"announcements", PermissionRead},
- }, everyoneGrants)
- // Ben: Before revoking
- require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite)) // Overwrite!
- require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
- require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
- require.Nil(t, a.Authorize(ben, "mytopic", PermissionRead))
- require.Nil(t, a.Authorize(ben, "mytopic", PermissionWrite))
- require.Nil(t, a.Authorize(ben, "readme", PermissionRead))
- require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite))
- // Revoke access for "ben" to "mytopic", then check again
- require.Nil(t, a.ResetAccess("ben", "mytopic"))
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionWrite)) // Revoked
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "mytopic", PermissionRead)) // Revoked
- require.Nil(t, a.Authorize(ben, "readme", PermissionRead)) // Unchanged
- require.Nil(t, a.Authorize(ben, "writeme", PermissionWrite)) // Unchanged
- // Revoke rest of the access
- require.Nil(t, a.ResetAccess("ben", ""))
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "readme", PermissionRead)) // Revoked
- require.Equal(t, ErrUnauthorized, a.Authorize(ben, "wrtiteme", PermissionWrite)) // Revoked
- // User list
- users, err := a.Users()
- require.Nil(t, err)
- require.Equal(t, 3, len(users))
- require.Equal(t, "phil", users[0].Name)
- require.Equal(t, "ben", users[1].Name)
- require.Equal(t, "*", users[2].Name)
- // Remove user
- require.Nil(t, a.RemoveUser("ben"))
- _, err = a.User("ben")
- require.Equal(t, ErrUserNotFound, err)
- users, err = a.Users()
- require.Nil(t, err)
- require.Equal(t, 2, len(users))
- require.Equal(t, "phil", users[0].Name)
- require.Equal(t, "*", users[1].Name)
- }
- func TestManager_ChangePassword(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("phil", "phil", RoleAdmin, "unit-test"))
- _, err := a.Authenticate("phil", "phil")
- require.Nil(t, err)
- require.Nil(t, a.ChangePassword("phil", "newpass"))
- _, err = a.Authenticate("phil", "phil")
- require.Equal(t, ErrUnauthenticated, err)
- _, err = a.Authenticate("phil", "newpass")
- require.Nil(t, err)
- }
- func TestManager_ChangeRole(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
- require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
- ben, err := a.User("ben")
- require.Nil(t, err)
- require.Equal(t, RoleUser, ben.Role)
- benGrants, err := a.Grants("ben")
- require.Nil(t, err)
- require.Equal(t, 2, len(benGrants))
- require.Nil(t, a.ChangeRole("ben", RoleAdmin))
- ben, err = a.User("ben")
- require.Nil(t, err)
- require.Equal(t, RoleAdmin, ben.Role)
- benGrants, err = a.Grants("ben")
- require.Nil(t, err)
- require.Equal(t, 0, len(benGrants))
- }
- func TestManager_Reservations(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- require.Nil(t, a.ReserveAccess("ben", "ztopic", PermissionDenyAll))
- require.Nil(t, a.ReserveAccess("ben", "readme", PermissionRead))
- require.Nil(t, a.AllowAccess("ben", "something-else", PermissionRead))
- reservations, err := a.Reservations("ben")
- require.Nil(t, err)
- require.Equal(t, 2, len(reservations))
- require.Equal(t, Reservation{
- Topic: "readme",
- Owner: PermissionReadWrite,
- Everyone: PermissionRead,
- }, reservations[0])
- require.Equal(t, Reservation{
- Topic: "ztopic",
- Owner: PermissionReadWrite,
- Everyone: PermissionDenyAll,
- }, reservations[1])
- }
- func TestManager_ChangeRoleFromTierUserToAdmin(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.CreateTier(&Tier{
- Code: "pro",
- Name: "ntfy Pro",
- StripePriceID: "price123",
- MessagesLimit: 5_000,
- MessagesExpiryDuration: 3 * 24 * time.Hour,
- EmailsLimit: 50,
- ReservationsLimit: 5,
- AttachmentFileSizeLimit: 52428800,
- AttachmentTotalSizeLimit: 524288000,
- AttachmentExpiryDuration: 24 * time.Hour,
- }))
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- require.Nil(t, a.ChangeTier("ben", "pro"))
- require.Nil(t, a.ReserveAccess("ben", "mytopic", PermissionDenyAll))
- ben, err := a.User("ben")
- require.Nil(t, err)
- require.Equal(t, RoleUser, ben.Role)
- require.Equal(t, "pro", ben.Tier.Code)
- require.Equal(t, true, ben.Tier.Paid)
- require.Equal(t, int64(5000), ben.Tier.MessagesLimit)
- require.Equal(t, 3*24*time.Hour, ben.Tier.MessagesExpiryDuration)
- require.Equal(t, int64(50), ben.Tier.EmailsLimit)
- require.Equal(t, int64(5), ben.Tier.ReservationsLimit)
- require.Equal(t, int64(52428800), ben.Tier.AttachmentFileSizeLimit)
- require.Equal(t, int64(524288000), ben.Tier.AttachmentTotalSizeLimit)
- require.Equal(t, 24*time.Hour, ben.Tier.AttachmentExpiryDuration)
- benGrants, err := a.Grants("ben")
- require.Nil(t, err)
- require.Equal(t, 1, len(benGrants))
- require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
- everyoneGrants, err := a.Grants(Everyone)
- require.Nil(t, err)
- require.Equal(t, 1, len(everyoneGrants))
- require.Equal(t, PermissionDenyAll, everyoneGrants[0].Allow)
- benReservations, err := a.Reservations("ben")
- require.Nil(t, err)
- require.Equal(t, 1, len(benReservations))
- require.Equal(t, "mytopic", benReservations[0].Topic)
- require.Equal(t, PermissionReadWrite, benReservations[0].Owner)
- require.Equal(t, PermissionDenyAll, benReservations[0].Everyone)
- // Switch to admin, this should remove all grants and owned ACL entries
- require.Nil(t, a.ChangeRole("ben", RoleAdmin))
- benGrants, err = a.Grants("ben")
- require.Nil(t, err)
- require.Equal(t, 0, len(benGrants))
- everyoneGrants, err = a.Grants(Everyone)
- require.Nil(t, err)
- require.Equal(t, 0, len(everyoneGrants))
- }
- func TestManager_Token_Valid(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- u, err := a.User("ben")
- require.Nil(t, err)
- // Create token for user
- token, err := a.CreateToken(u)
- require.Nil(t, err)
- require.NotEmpty(t, token.Value)
- require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires.Unix())
- u2, err := a.AuthenticateToken(token.Value)
- require.Nil(t, err)
- require.Equal(t, u.Name, u2.Name)
- require.Equal(t, token.Value, u2.Token)
- // Remove token and auth again
- require.Nil(t, a.RemoveToken(u2))
- u3, err := a.AuthenticateToken(token.Value)
- require.Equal(t, ErrUnauthenticated, err)
- require.Nil(t, u3)
- }
- func TestManager_Token_Invalid(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- u, err := a.AuthenticateToken(strings.Repeat("x", 32)) // 32 == token length
- require.Nil(t, u)
- require.Equal(t, ErrUnauthenticated, err)
- u, err = a.AuthenticateToken("not long enough anyway")
- require.Nil(t, u)
- require.Equal(t, ErrUnauthenticated, err)
- }
- func TestManager_Token_Expire(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- u, err := a.User("ben")
- require.Nil(t, err)
- // Create tokens for user
- token1, err := a.CreateToken(u)
- require.Nil(t, err)
- require.NotEmpty(t, token1.Value)
- require.True(t, time.Now().Add(71*time.Hour).Unix() < token1.Expires.Unix())
- token2, err := a.CreateToken(u)
- require.Nil(t, err)
- require.NotEmpty(t, token2.Value)
- require.NotEqual(t, token1.Value, token2.Value)
- require.True(t, time.Now().Add(71*time.Hour).Unix() < token2.Expires.Unix())
- // See that tokens work
- _, err = a.AuthenticateToken(token1.Value)
- require.Nil(t, err)
- _, err = a.AuthenticateToken(token2.Value)
- require.Nil(t, err)
- // Modify token expiration in database
- _, err = a.db.Exec("UPDATE user_token SET expires = 1 WHERE token = ?", token1.Value)
- require.Nil(t, err)
- // Now token1 shouldn't work anymore
- _, err = a.AuthenticateToken(token1.Value)
- require.Equal(t, ErrUnauthenticated, err)
- result, err := a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
- require.Nil(t, err)
- require.True(t, result.Next()) // Still a matching row
- require.Nil(t, result.Close())
- // Expire tokens and check database rows
- require.Nil(t, a.RemoveExpiredTokens())
- result, err = a.db.Query("SELECT * from user_token WHERE token = ?", token1.Value)
- require.Nil(t, err)
- require.False(t, result.Next()) // No matching row!
- require.Nil(t, result.Close())
- }
- func TestManager_Token_Extend(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- // Try to extend token for user without token
- u, err := a.User("ben")
- require.Nil(t, err)
- _, err = a.ExtendToken(u)
- require.Equal(t, errNoTokenProvided, err)
- // Create token for user
- token, err := a.CreateToken(u)
- require.Nil(t, err)
- require.NotEmpty(t, token.Value)
- userWithToken, err := a.AuthenticateToken(token.Value)
- require.Nil(t, err)
- time.Sleep(1100 * time.Millisecond)
- extendedToken, err := a.ExtendToken(userWithToken)
- require.Nil(t, err)
- require.Equal(t, token.Value, extendedToken.Value)
- require.True(t, token.Expires.Unix() < extendedToken.Expires.Unix())
- }
- func TestManager_Token_MaxCount_AutoDelete(t *testing.T) {
- a := newTestManager(t, PermissionDenyAll)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- // Try to extend token for user without token
- u, err := a.User("ben")
- require.Nil(t, err)
- // Tokens
- baseTime := time.Now().Add(24 * time.Hour)
- tokens := make([]string, 0)
- for i := 0; i < 12; i++ {
- token, err := a.CreateToken(u)
- require.Nil(t, err)
- require.NotEmpty(t, token.Value)
- tokens = append(tokens, token.Value)
- // Manually modify expiry date to avoid sorting issues (this is a hack)
- _, err = a.db.Exec(`UPDATE user_token SET expires=? WHERE token=?`, baseTime.Add(time.Duration(i)*time.Minute).Unix(), token.Value)
- require.Nil(t, err)
- }
- _, err = a.AuthenticateToken(tokens[0])
- require.Equal(t, ErrUnauthenticated, err)
- _, err = a.AuthenticateToken(tokens[1])
- require.Equal(t, ErrUnauthenticated, err)
- for i := 2; i < 12; i++ {
- userWithToken, err := a.AuthenticateToken(tokens[i])
- require.Nil(t, err, "token[%d]=%s failed", i, tokens[i])
- require.Equal(t, "ben", userWithToken.Name)
- require.Equal(t, tokens[i], userWithToken.Token)
- }
- var count int
- rows, err := a.db.Query(`SELECT COUNT(*) FROM user_token`)
- require.Nil(t, err)
- require.True(t, rows.Next())
- require.Nil(t, rows.Scan(&count))
- require.Equal(t, 10, count)
- }
- func TestManager_EnqueueStats(t *testing.T) {
- a, err := newManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, 1500*time.Millisecond)
- require.Nil(t, err)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- // Baseline: No messages or emails
- u, err := a.User("ben")
- require.Nil(t, err)
- require.Equal(t, int64(0), u.Stats.Messages)
- require.Equal(t, int64(0), u.Stats.Emails)
- u.Stats.Messages = 11
- u.Stats.Emails = 2
- a.EnqueueStats(u)
- // Still no change, because it's queued asynchronously
- u, err = a.User("ben")
- require.Nil(t, err)
- require.Equal(t, int64(0), u.Stats.Messages)
- require.Equal(t, int64(0), u.Stats.Emails)
- // After 2 seconds they should be persisted
- time.Sleep(2 * time.Second)
- u, err = a.User("ben")
- require.Nil(t, err)
- require.Equal(t, int64(11), u.Stats.Messages)
- require.Equal(t, int64(2), u.Stats.Emails)
- }
- func TestManager_ChangeSettings(t *testing.T) {
- a, err := newManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, 1500*time.Millisecond)
- require.Nil(t, err)
- require.Nil(t, a.AddUser("ben", "ben", RoleUser, "unit-test"))
- // No settings
- u, err := a.User("ben")
- require.Nil(t, err)
- require.Nil(t, u.Prefs.Subscriptions)
- require.Nil(t, u.Prefs.Notification)
- require.Equal(t, "", u.Prefs.Language)
- // Save with new settings
- u.Prefs = &Prefs{
- Language: "de",
- Notification: &NotificationPrefs{
- Sound: "ding",
- MinPriority: 2,
- },
- Subscriptions: []*Subscription{
- {
- ID: "someID",
- BaseURL: "https://ntfy.sh",
- Topic: "mytopic",
- DisplayName: "My Topic",
- },
- },
- }
- require.Nil(t, a.ChangeSettings(u))
- // Read again
- u, err = a.User("ben")
- require.Nil(t, err)
- require.Equal(t, "de", u.Prefs.Language)
- require.Equal(t, "ding", u.Prefs.Notification.Sound)
- require.Equal(t, 2, u.Prefs.Notification.MinPriority)
- require.Equal(t, 0, u.Prefs.Notification.DeleteAfter)
- require.Equal(t, "someID", u.Prefs.Subscriptions[0].ID)
- require.Equal(t, "https://ntfy.sh", u.Prefs.Subscriptions[0].BaseURL)
- require.Equal(t, "mytopic", u.Prefs.Subscriptions[0].Topic)
- require.Equal(t, "My Topic", u.Prefs.Subscriptions[0].DisplayName)
- }
- func TestSqliteCache_Migration_From1(t *testing.T) {
- filename := filepath.Join(t.TempDir(), "user.db")
- db, err := sql.Open("sqlite3", filename)
- require.Nil(t, err)
- // Create "version 1" schema
- _, err = db.Exec(`
- BEGIN;
- CREATE TABLE IF NOT EXISTS user (
- user TEXT NOT NULL PRIMARY KEY,
- pass TEXT NOT NULL,
- role TEXT NOT NULL
- );
- CREATE TABLE IF NOT EXISTS access (
- user TEXT NOT NULL,
- topic TEXT NOT NULL,
- read INT NOT NULL,
- write INT NOT NULL,
- PRIMARY KEY (topic, user)
- );
- CREATE TABLE IF NOT EXISTS schemaVersion (
- id INT PRIMARY KEY,
- version INT NOT NULL
- );
- INSERT INTO schemaVersion (id, version) VALUES (1, 1);
- COMMIT;
- `)
- require.Nil(t, err)
- // Insert a bunch of users and ACL entries
- _, err = db.Exec(`
- BEGIN;
- INSERT INTO user (user, pass, role) VALUES ('ben', '$2a$10$EEp6gBheOsqEFsXlo523E.gBVoeg1ytphXiEvTPlNzkenBlHZBPQy', 'user');
- INSERT INTO user (user, pass, role) VALUES ('phil', '$2a$10$YLiO8U21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C', 'admin');
- INSERT INTO access (user, topic, read, write) VALUES ('ben', 'stats', 1, 1);
- INSERT INTO access (user, topic, read, write) VALUES ('ben', 'secret', 1, 0);
- INSERT INTO access (user, topic, read, write) VALUES ('*', 'stats', 1, 0);
- COMMIT;
- `)
- require.Nil(t, err)
- // Create manager to trigger migration
- a := newTestManagerFromFile(t, filename, "", PermissionDenyAll, userStatsQueueWriterInterval)
- checkSchemaVersion(t, a.db)
- users, err := a.Users()
- require.Nil(t, err)
- require.Equal(t, 3, len(users))
- phil, ben, everyone := users[0], users[1], users[2]
- philGrants, err := a.Grants("phil")
- require.Nil(t, err)
- benGrants, err := a.Grants("ben")
- require.Nil(t, err)
- everyoneGrants, err := a.Grants(Everyone)
- require.Nil(t, err)
- require.Equal(t, "phil", phil.Name)
- require.Equal(t, RoleAdmin, phil.Role)
- require.Equal(t, syncTopicLength, len(phil.SyncTopic))
- require.Equal(t, 0, len(philGrants))
- require.Equal(t, "ben", ben.Name)
- require.Equal(t, RoleUser, ben.Role)
- require.Equal(t, syncTopicLength, len(ben.SyncTopic))
- require.NotEqual(t, ben.SyncTopic, phil.SyncTopic)
- require.Equal(t, 2, len(benGrants))
- require.Equal(t, "stats", benGrants[0].TopicPattern)
- require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
- require.Equal(t, "secret", benGrants[1].TopicPattern)
- require.Equal(t, PermissionRead, benGrants[1].Allow)
- require.Equal(t, Everyone, everyone.Name)
- require.Equal(t, RoleAnonymous, everyone.Role)
- require.Equal(t, 1, len(everyoneGrants))
- require.Equal(t, "stats", everyoneGrants[0].TopicPattern)
- require.Equal(t, PermissionRead, everyoneGrants[0].Allow)
- }
- func checkSchemaVersion(t *testing.T, db *sql.DB) {
- rows, err := db.Query(`SELECT version FROM schemaVersion`)
- require.Nil(t, err)
- require.True(t, rows.Next())
- var schemaVersion int
- require.Nil(t, rows.Scan(&schemaVersion))
- require.Equal(t, currentSchemaVersion, schemaVersion)
- require.Nil(t, rows.Close())
- }
- func newTestManager(t *testing.T, defaultAccess Permission) *Manager {
- return newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", defaultAccess, userStatsQueueWriterInterval)
- }
- func newTestManagerFromFile(t *testing.T, filename, startupQueries string, defaultAccess Permission, statsWriterInterval time.Duration) *Manager {
- a, err := newManager(filename, startupQueries, defaultAccess, statsWriterInterval)
- require.Nil(t, err)
- return a
- }
|