User management improvements (#1101)

* Show more descriptive success messages for User actions

* Check username uniqueness when creating/updating User

* Adjust translations

* Add tests for `validateUsernameUnique()`

Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Steve Richter
2021-05-16 13:25:38 -04:00
committed by GitHub
parent 666c006579
commit e60f2bfa3d
8 changed files with 137 additions and 28 deletions
+20
View File
@@ -134,6 +134,9 @@ func (r *userRepository) Save(entity interface{}) (string, error) {
return "", rest.ErrPermissionDenied
}
u := entity.(*model.User)
if err := validateUsernameUnique(r, u); err != nil {
return "", err
}
err := r.Put(u)
if err != nil {
return "", err
@@ -157,6 +160,9 @@ func (r *userRepository) Update(entity interface{}, cols ...string) error {
if err := validatePasswordChange(u, usr); err != nil {
return err
}
if err := validateUsernameUnique(r, u); err != nil {
return err
}
err := r.Put(u)
if err == model.ErrNotFound {
return rest.ErrNotFound
@@ -186,6 +192,20 @@ func validatePasswordChange(newUser *model.User, logged *model.User) error {
return nil
}
func validateUsernameUnique(r model.UserRepository, u *model.User) error {
usr, err := r.FindByUsername(u.UserName)
if err == model.ErrNotFound {
return nil
}
if err != nil {
return err
}
if usr.ID != u.ID {
return &rest.ValidationError{Errors: map[string]string{"userName": "ra.validation.unique"}}
}
return nil
}
func (r *userRepository) Delete(id string) error {
usr := loggedUser(r.ctx)
if !usr.IsAdmin {
+30
View File
@@ -2,11 +2,13 @@ package persistence
import (
"context"
"errors"
"github.com/astaxie/beego/orm"
"github.com/deluan/rest"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
@@ -144,4 +146,32 @@ var _ = Describe("UserRepository", func() {
})
})
})
Describe("validateUsernameUnique", func() {
var repo *tests.MockedUserRepo
var existingUser *model.User
BeforeEach(func() {
existingUser = &model.User{ID: "1", UserName: "johndoe"}
repo = tests.CreateMockUserRepo()
err := repo.Put(existingUser)
Expect(err).ToNot(HaveOccurred())
})
It("allows unique usernames", func() {
var newUser = &model.User{ID: "2", UserName: "unique_username"}
err := validateUsernameUnique(repo, newUser)
Expect(err).ToNot(HaveOccurred())
})
It("returns ValidationError if username already exists", func() {
var newUser = &model.User{ID: "2", UserName: "johndoe"}
err := validateUsernameUnique(repo, newUser)
Expect(err).To(BeAssignableToTypeOf(&rest.ValidationError{}))
Expect(err.(*rest.ValidationError).Errors).To(HaveKeyWithValue("userName", "ra.validation.unique"))
})
It("returns generic error if repository call fails", func() {
repo.Err = errors.New("fake error")
var newUser = &model.User{ID: "2", UserName: "newuser"}
err := validateUsernameUnique(repo, newUser)
Expect(err).To(MatchError("fake error"))
})
})
})