mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-05 05:27:37 +03:00
Fix DevAutoCreateAdminPassword
This commit is contained in:
parent
88105d5c30
commit
e3fe8399c8
6 changed files with 93 additions and 30 deletions
|
@ -7,12 +7,16 @@ type User struct {
|
||||||
UserName string `json:"userName"`
|
UserName string `json:"userName"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"-"`
|
|
||||||
IsAdmin bool `json:"isAdmin"`
|
IsAdmin bool `json:"isAdmin"`
|
||||||
LastLoginAt *time.Time `json:"lastLoginAt"`
|
LastLoginAt *time.Time `json:"lastLoginAt"`
|
||||||
LastAccessAt *time.Time `json:"lastAccessAt"`
|
LastAccessAt *time.Time `json:"lastAccessAt"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
|
||||||
|
// This is only available on the backend, and it is never sent over the wire
|
||||||
|
Password string `json:"-"`
|
||||||
|
// This is used to set or change a password when calling Put. If it is empty, the password is not changed.
|
||||||
|
// It is received from the UI with the name "password"
|
||||||
NewPassword string `json:"password,omitempty"`
|
NewPassword string `json:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ var _ = Describe("UserRepository", func() {
|
||||||
UserName: "AdMiN",
|
UserName: "AdMiN",
|
||||||
Name: "Admin",
|
Name: "Admin",
|
||||||
Email: "admin@admin.com",
|
Email: "admin@admin.com",
|
||||||
Password: "wordpass",
|
NewPassword: "wordpass",
|
||||||
IsAdmin: true,
|
IsAdmin: true,
|
||||||
}
|
}
|
||||||
It("saves the user to the DB", func() {
|
It("saves the user to the DB", func() {
|
||||||
|
@ -33,6 +33,7 @@ var _ = Describe("UserRepository", func() {
|
||||||
actual, err := repo.Get("123")
|
actual, err := repo.Get("123")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(actual.Name).To(Equal("Admin"))
|
Expect(actual.Name).To(Equal("Admin"))
|
||||||
|
Expect(actual.Password).To(Equal("wordpass"))
|
||||||
})
|
})
|
||||||
It("find the user by case-insensitive username", func() {
|
It("find the user by case-insensitive username", func() {
|
||||||
actual, err := repo.FindByUsername("aDmIn")
|
actual, err := repo.FindByUsername("aDmIn")
|
||||||
|
|
|
@ -51,7 +51,7 @@ func createInitialAdminUser(ds model.DataStore, initialPassword string) error {
|
||||||
UserName: consts.DevInitialUserName,
|
UserName: consts.DevInitialUserName,
|
||||||
Name: consts.DevInitialName,
|
Name: consts.DevInitialName,
|
||||||
Email: "",
|
Email: "",
|
||||||
Password: initialPassword,
|
NewPassword: initialPassword,
|
||||||
IsAdmin: true,
|
IsAdmin: true,
|
||||||
}
|
}
|
||||||
err := users.Put(&initialUser)
|
err := users.Put(&initialUser)
|
||||||
|
|
36
server/initial_setup_test.go
Normal file
36
server/initial_setup_test.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/navidrome/navidrome/model"
|
||||||
|
"github.com/navidrome/navidrome/tests"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("initial_setup", func() {
|
||||||
|
var ds model.DataStore
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
ds = &tests.MockDataStore{}
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("createInitialAdminUser", func() {
|
||||||
|
It("creates a new admin user with specified password if User table is empty", func() {
|
||||||
|
Expect(createInitialAdminUser(ds, "pass123")).To(BeNil())
|
||||||
|
ur := ds.User(context.TODO())
|
||||||
|
admin, err := ur.FindByUsername("admin")
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(admin.Password).To(Equal("pass123"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("does not create a new admin user if User table is not empty", func() {
|
||||||
|
Expect(createInitialAdminUser(ds, "first")).To(BeNil())
|
||||||
|
ur := ds.User(context.TODO())
|
||||||
|
Expect(ur.CountAll()).To(Equal(int64(1)))
|
||||||
|
Expect(createInitialAdminUser(ds, "second")).To(BeNil())
|
||||||
|
Expect(ur.CountAll()).To(Equal(int64(1)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -36,10 +36,12 @@ func newPostRequest(queryParam string, formFields ...string) *http.Request {
|
||||||
var _ = Describe("Middlewares", func() {
|
var _ = Describe("Middlewares", func() {
|
||||||
var next *mockHandler
|
var next *mockHandler
|
||||||
var w *httptest.ResponseRecorder
|
var w *httptest.ResponseRecorder
|
||||||
|
var ds model.DataStore
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
next = &mockHandler{}
|
next = &mockHandler{}
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
|
ds = &tests.MockDataStore{}
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("ParsePostForm", func() {
|
Describe("ParsePostForm", func() {
|
||||||
|
@ -115,11 +117,13 @@ var _ = Describe("Middlewares", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Authenticate", func() {
|
Describe("Authenticate", func() {
|
||||||
var ds model.DataStore
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
ds = &tests.MockDataStore{}
|
ur := ds.User(context.TODO())
|
||||||
|
_ = ur.Put(&model.User{
|
||||||
|
UserName: "admin",
|
||||||
|
NewPassword: "wordpass",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
It("passes authentication with correct credentials", func() {
|
It("passes authentication with correct credentials", func() {
|
||||||
r := newGetRequest("u=admin", "p=wordpass")
|
r := newGetRequest("u=admin", "p=wordpass")
|
||||||
cp := authenticate(ds)(next)
|
cp := authenticate(ds)(next)
|
||||||
|
@ -220,16 +224,18 @@ var _ = Describe("Middlewares", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("validateUser", func() {
|
Describe("validateUser", func() {
|
||||||
var ds model.DataStore
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
ds = &tests.MockDataStore{}
|
ur := ds.User(context.TODO())
|
||||||
|
_ = ur.Put(&model.User{
|
||||||
|
UserName: "admin",
|
||||||
|
NewPassword: "wordpass",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("Plaintext password", func() {
|
Context("Plaintext password", func() {
|
||||||
It("authenticates with plaintext password ", func() {
|
It("authenticates with plaintext password ", func() {
|
||||||
usr, err := validateUser(context.TODO(), ds, "admin", "wordpass", "", "", "")
|
usr, err := validateUser(context.TODO(), ds, "admin", "wordpass", "", "", "")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(usr).To(Equal(&model.User{UserName: "admin", Password: "wordpass"}))
|
Expect(usr.UserName).To(Equal("admin"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("fails authentication with wrong password", func() {
|
It("fails authentication with wrong password", func() {
|
||||||
|
@ -242,7 +248,7 @@ var _ = Describe("Middlewares", func() {
|
||||||
It("authenticates with simple encoded password ", func() {
|
It("authenticates with simple encoded password ", func() {
|
||||||
usr, err := validateUser(context.TODO(), ds, "admin", "enc:776f726470617373", "", "", "")
|
usr, err := validateUser(context.TODO(), ds, "admin", "enc:776f726470617373", "", "", "")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(usr).To(Equal(&model.User{UserName: "admin", Password: "wordpass"}))
|
Expect(usr.UserName).To(Equal("admin"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -250,7 +256,7 @@ var _ = Describe("Middlewares", func() {
|
||||||
It("authenticates with token based authentication", func() {
|
It("authenticates with token based authentication", func() {
|
||||||
usr, err := validateUser(context.TODO(), ds, "admin", "", "23b342970e25c7928831c3317edd0b67", "retnlmjetrymazgkt", "")
|
usr, err := validateUser(context.TODO(), ds, "admin", "", "23b342970e25c7928831c3317edd0b67", "retnlmjetrymazgkt", "")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(usr).To(Equal(&model.User{UserName: "admin", Password: "wordpass"}))
|
Expect(usr.UserName).To(Equal("admin"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("fails if salt is missing", func() {
|
It("fails if salt is missing", func() {
|
||||||
|
@ -273,7 +279,7 @@ var _ = Describe("Middlewares", func() {
|
||||||
usr, err := validateUser(context.TODO(), ds, "admin", "", "", "", validToken)
|
usr, err := validateUser(context.TODO(), ds, "admin", "", "", "", validToken)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(usr).To(Equal(&model.User{UserName: "admin", Password: "wordpass"}))
|
Expect(usr.UserName).To(Equal("admin"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("fails if JWT token is invalid", func() {
|
It("fails if JWT token is invalid", func() {
|
||||||
|
|
|
@ -2,6 +2,8 @@ package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/navidrome/navidrome/model"
|
"github.com/navidrome/navidrome/model"
|
||||||
)
|
)
|
||||||
|
@ -95,15 +97,29 @@ func (db *MockDataStore) GC(ctx context.Context, rootFolder string) error {
|
||||||
|
|
||||||
type mockedUserRepo struct {
|
type mockedUserRepo struct {
|
||||||
model.UserRepository
|
model.UserRepository
|
||||||
|
data map[string]*model.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *mockedUserRepo) CountAll(qo ...model.QueryOptions) (int64, error) {
|
||||||
|
return int64(len(u.data)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *mockedUserRepo) Put(usr *model.User) error {
|
||||||
|
if u.data == nil {
|
||||||
|
u.data = make(map[string]*model.User)
|
||||||
|
}
|
||||||
|
if usr.ID == "" {
|
||||||
|
usr.ID = base64.StdEncoding.EncodeToString([]byte(usr.UserName))
|
||||||
|
}
|
||||||
|
usr.Password = usr.NewPassword
|
||||||
|
u.data[strings.ToLower(usr.UserName)] = usr
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *mockedUserRepo) FindByUsername(username string) (*model.User, error) {
|
func (u *mockedUserRepo) FindByUsername(username string) (*model.User, error) {
|
||||||
if username != "admin" {
|
usr, ok := u.data[strings.ToLower(username)]
|
||||||
|
if !ok {
|
||||||
return nil, model.ErrNotFound
|
return nil, model.ErrNotFound
|
||||||
}
|
}
|
||||||
return &model.User{UserName: "admin", Password: "wordpass"}, nil
|
return usr, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (u *mockedUserRepo) UpdateLastAccessAt(id string) error {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue