diff --git a/server/server_account_test.go b/server/server_account_test.go index d4411418..819c91ed 100644 --- a/server/server_account_test.go +++ b/server/server_account_test.go @@ -269,6 +269,43 @@ func TestAccount_ExtendToken_NoTokenProvided(t *testing.T) { require.Equal(t, 40023, toHTTPError(t, rr.Body.String()).Code) } +func TestAccount_DeleteToken(t *testing.T) { + s := newTestServer(t, newTestConfigWithUsers(t)) + require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser)) + + rr := request(t, s, "POST", "/v1/account/token", "", map[string]string{ + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, rr.Code) + token, err := util.ReadJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) + require.Nil(t, err) + + // Delete token failure (using basic auth) + rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{ + "Authorization": util.BasicAuth("phil", "phil"), // Not Bearer! + }) + require.Equal(t, 400, rr.Code) + require.Equal(t, 40023, toHTTPError(t, rr.Body.String()).Code) + + // Delete token with wrong token + rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{ + "Authorization": util.BearerAuth("invalidtoken"), + }) + require.Equal(t, 401, rr.Code) + + // Delete token with correct token + rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{ + "Authorization": util.BearerAuth(token.Token), + }) + require.Equal(t, 200, rr.Code) + + // Cannot get account anymore + rr = request(t, s, "GET", "/v1/account", "", map[string]string{ + "Authorization": util.BearerAuth(token.Token), + }) + require.Equal(t, 401, rr.Code) +} + func TestAccount_Delete_Success(t *testing.T) { conf := newTestConfigWithUsers(t) conf.EnableSignup = true diff --git a/user/manager.go b/user/manager.go index 54912f17..e71ae40e 100644 --- a/user/manager.go +++ b/user/manager.go @@ -22,6 +22,10 @@ const ( userTokenExpiryDuration = 72 * time.Hour ) +var ( + errNoTokenProvided = errors.New("no token provided") +) + // Manager-related queries const ( createTablesQueriesNoTx = ` @@ -239,6 +243,9 @@ func (a *Manager) CreateToken(user *User) (*Token, error) { // ExtendToken sets the new expiry date for a token, thereby extending its use further into the future. func (a *Manager) ExtendToken(user *User) (*Token, error) { + if user.Token == "" { + return nil, errNoTokenProvided + } newExpires := time.Now().Add(userTokenExpiryDuration) if _, err := a.db.Exec(updateTokenExpiryQuery, newExpires.Unix(), user.Name, user.Token); err != nil { return nil, err diff --git a/user/manager_test.go b/user/manager_test.go index c9669ad1..9a565a81 100644 --- a/user/manager_test.go +++ b/user/manager_test.go @@ -321,6 +321,33 @@ func TestManager_Token_Expire(t *testing.T) { require.Nil(t, result.Close()) } +func TestManager_Token_Extend(t *testing.T) { + a := newTestManager(t, false, false) + require.Nil(t, a.AddUser("ben", "ben", RoleUser)) + + // 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_EnqueueStats(t *testing.T) { a, err := newManager(filepath.Join(t.TempDir(), "db"), true, true, time.Hour, 1500*time.Millisecond) require.Nil(t, err) @@ -351,6 +378,47 @@ func TestManager_EnqueueStats(t *testing.T) { require.Equal(t, int64(2), u.Stats.Emails) } +func TestManager_ChangeSettings(t *testing.T) { + a, err := newManager(filepath.Join(t.TempDir(), "db"), true, true, time.Hour, 1500*time.Millisecond) + require.Nil(t, err) + require.Nil(t, a.AddUser("ben", "ben", RoleUser)) + + // No settings + u, err := a.User("ben") + require.Nil(t, err) + require.Nil(t, u.Prefs) + + // 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)