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:
@@ -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 {
|
||||
|
||||
@@ -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"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user