Predefined users

This commit is contained in:
binwiederhier
2025-07-10 20:50:29 +02:00
parent efef587671
commit c0b5151bae
8 changed files with 157 additions and 67 deletions

View File

@@ -449,13 +449,13 @@ type Manager struct {
}
type Config struct {
Filename string
StartupQueries string
Filename string // Database filename, e.g. "/var/lib/ntfy/user.db"
StartupQueries string // Queries to run on startup, e.g. to create initial users or tiers
DefaultAccess Permission // Default permission if no ACL matches
ProvisionedUsers []*User // Predefined users to create on startup
ProvisionedAccess map[string][]*Grant // Predefined access grants to create on startup
BcryptCost int // Makes testing easier
QueueWriterInterval time.Duration
QueueWriterInterval time.Duration // Interval for the async queue writer to flush stats and token updates to the database
BcryptCost int // Cost of generated passwords; lowering makes testing faster
}
var _ Auther = (*Manager)(nil)
@@ -469,7 +469,6 @@ func NewManager(config *Config) (*Manager, error) {
if config.QueueWriterInterval.Seconds() <= 0 {
config.QueueWriterInterval = DefaultUserStatsQueueWriterInterval
}
// Open DB and run setup queries
db, err := sql.Open("sqlite3", config.Filename)
if err != nil {
@@ -487,6 +486,9 @@ func NewManager(config *Config) (*Manager, error) {
statsQueue: make(map[string]*Stats),
tokenQueue: make(map[string]*TokenUpdate),
}
if err := manager.provisionUsers(); err != nil {
return nil, err
}
go manager.asyncQueueWriter(config.QueueWriterInterval)
return manager, nil
}
@@ -1522,6 +1524,22 @@ func (a *Manager) Close() error {
return a.db.Close()
}
func (a *Manager) provisionUsers() error {
for _, user := range a.config.ProvisionedUsers {
if err := a.AddUser(user.Name, user.Hash, user.Role, true); err != nil && !errors.Is(err, ErrUserExists) {
return err
}
}
for username, grants := range a.config.ProvisionedAccess {
for _, grant := range grants {
if err := a.AllowAccess(username, grant.TopicPattern, grant.Allow); err != nil {
return err
}
}
}
return nil
}
// toSQLWildcard converts a wildcard string to a SQL wildcard string. It only allows '*' as wildcards,
// and escapes '_', assuming '\' as escape character.
func toSQLWildcard(s string) string {

View File

@@ -731,7 +731,14 @@ func TestManager_Token_MaxCount_AutoDelete(t *testing.T) {
}
func TestManager_EnqueueStats_ResetStats(t *testing.T) {
a, err := NewManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, bcrypt.MinCost, 1500*time.Millisecond)
conf := &Config{
Filename: filepath.Join(t.TempDir(), "db"),
StartupQueries: "",
DefaultAccess: PermissionReadWrite,
BcryptCost: bcrypt.MinCost,
QueueWriterInterval: 1500 * time.Millisecond,
}
a, err := NewManager(conf)
require.Nil(t, err)
require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
@@ -773,7 +780,14 @@ func TestManager_EnqueueStats_ResetStats(t *testing.T) {
}
func TestManager_EnqueueTokenUpdate(t *testing.T) {
a, err := NewManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, bcrypt.MinCost, 500*time.Millisecond)
conf := &Config{
Filename: filepath.Join(t.TempDir(), "db"),
StartupQueries: "",
DefaultAccess: PermissionReadWrite,
BcryptCost: bcrypt.MinCost,
QueueWriterInterval: 500 * time.Millisecond,
}
a, err := NewManager(conf)
require.Nil(t, err)
require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
@@ -806,7 +820,14 @@ func TestManager_EnqueueTokenUpdate(t *testing.T) {
}
func TestManager_ChangeSettings(t *testing.T) {
a, err := NewManager(filepath.Join(t.TempDir(), "db"), "", PermissionReadWrite, bcrypt.MinCost, 1500*time.Millisecond)
conf := &Config{
Filename: filepath.Join(t.TempDir(), "db"),
StartupQueries: "",
DefaultAccess: PermissionReadWrite,
BcryptCost: bcrypt.MinCost,
QueueWriterInterval: 1500 * time.Millisecond,
}
a, err := NewManager(conf)
require.Nil(t, err)
require.Nil(t, a.AddUser("ben", "ben", RoleUser, false))
@@ -1075,6 +1096,24 @@ func TestManager_Topic_Wildcard_With_Underscore(t *testing.T) {
require.Equal(t, ErrUnauthorized, a.Authorize(nil, "mytopicX", PermissionWrite))
}
func TestManager_WithProvisionedUsers(t *testing.T) {
f := filepath.Join(t.TempDir(), "user.db")
conf := &Config{
Filename: f,
DefaultAccess: PermissionReadWrite,
ProvisionedUsers: []*User{
{Name: "phil", Hash: "$2a$10$YLiO8U21sX1uhZamTLJXHuxgVC0Z/GKISibrKCLohPgtG7yIxSk4C", Role: RoleAdmin},
},
}
a, err := NewManager(conf)
require.Nil(t, err)
users, err := a.Users()
require.Nil(t, err)
for _, u := range users {
fmt.Println(u.ID, u.Name, u.Role)
}
}
func TestToFromSQLWildcard(t *testing.T) {
require.Equal(t, "up%", toSQLWildcard("up*"))
require.Equal(t, "up\\_%", toSQLWildcard("up_*"))
@@ -1336,7 +1375,14 @@ func newTestManager(t *testing.T, defaultAccess Permission) *Manager {
}
func newTestManagerFromFile(t *testing.T, filename, startupQueries string, defaultAccess Permission, bcryptCost int, statsWriterInterval time.Duration) *Manager {
a, err := NewManager(filename, startupQueries, defaultAccess, bcryptCost, statsWriterInterval)
conf := &Config{
Filename: filename,
StartupQueries: startupQueries,
DefaultAccess: defaultAccess,
BcryptCost: bcryptCost,
QueueWriterInterval: statsWriterInterval,
}
a, err := NewManager(conf)
require.Nil(t, err)
return a
}