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

@@ -1,76 +1,70 @@
version: 2
before: before:
hooks: hooks:
- go mod download - go mod download
- go mod tidy - go mod tidy
builds: builds:
- - id: ntfy_linux_amd64
id: ntfy_linux_amd64
binary: ntfy binary: ntfy
env: env:
- CGO_ENABLED=1 # required for go-sqlite3 - CGO_ENABLED=1 # required for go-sqlite3
tags: [sqlite_omit_load_extension,osusergo,netgo] tags: [ sqlite_omit_load_extension,osusergo,netgo ]
ldflags: ldflags:
- "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" - "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
goos: [linux] goos: [ linux ]
goarch: [amd64] goarch: [ amd64 ]
- - id: ntfy_linux_armv6
id: ntfy_linux_armv6
binary: ntfy binary: ntfy
env: env:
- CGO_ENABLED=1 # required for go-sqlite3 - CGO_ENABLED=1 # required for go-sqlite3
- CC=arm-linux-gnueabi-gcc # apt install gcc-arm-linux-gnueabi - CC=arm-linux-gnueabi-gcc # apt install gcc-arm-linux-gnueabi
tags: [sqlite_omit_load_extension,osusergo,netgo] tags: [ sqlite_omit_load_extension,osusergo,netgo ]
ldflags: ldflags:
- "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" - "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
goos: [linux] goos: [ linux ]
goarch: [arm] goarch: [ arm ]
goarm: [6] goarm: [ 6 ]
- - id: ntfy_linux_armv7
id: ntfy_linux_armv7
binary: ntfy binary: ntfy
env: env:
- CGO_ENABLED=1 # required for go-sqlite3 - CGO_ENABLED=1 # required for go-sqlite3
- CC=arm-linux-gnueabi-gcc # apt install gcc-arm-linux-gnueabi - CC=arm-linux-gnueabi-gcc # apt install gcc-arm-linux-gnueabi
tags: [sqlite_omit_load_extension,osusergo,netgo] tags: [ sqlite_omit_load_extension,osusergo,netgo ]
ldflags: ldflags:
- "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" - "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
goos: [linux] goos: [ linux ]
goarch: [arm] goarch: [ arm ]
goarm: [7] goarm: [ 7 ]
- - id: ntfy_linux_arm64
id: ntfy_linux_arm64
binary: ntfy binary: ntfy
env: env:
- CGO_ENABLED=1 # required for go-sqlite3 - CGO_ENABLED=1 # required for go-sqlite3
- CC=aarch64-linux-gnu-gcc # apt install gcc-aarch64-linux-gnu - CC=aarch64-linux-gnu-gcc # apt install gcc-aarch64-linux-gnu
tags: [sqlite_omit_load_extension,osusergo,netgo] tags: [ sqlite_omit_load_extension,osusergo,netgo ]
ldflags: ldflags:
- "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" - "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
goos: [linux] goos: [ linux ]
goarch: [arm64] goarch: [ arm64 ]
- - id: ntfy_windows_amd64
id: ntfy_windows_amd64
binary: ntfy binary: ntfy
env: env:
- CGO_ENABLED=0 # explicitly disable, since we don't need go-sqlite3 - CGO_ENABLED=0 # explicitly disable, since we don't need go-sqlite3
tags: [noserver] # don't include server files tags: [ noserver ] # don't include server files
ldflags: ldflags:
- "-X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" - "-X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
goos: [windows] goos: [ windows ]
goarch: [amd64] goarch: [ amd64 ]
- - id: ntfy_darwin_all
id: ntfy_darwin_all
binary: ntfy binary: ntfy
env: env:
- CGO_ENABLED=0 # explicitly disable, since we don't need go-sqlite3 - CGO_ENABLED=0 # explicitly disable, since we don't need go-sqlite3
tags: [noserver] # don't include server files tags: [ noserver ] # don't include server files
ldflags: ldflags:
- "-X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" - "-X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
goos: [darwin] goos: [ darwin ]
goarch: [amd64, arm64] # will be combined to "universal binary" (see below) goarch: [ amd64, arm64 ] # will be combined to "universal binary" (see below)
nfpms: nfpms:
- - package_name: ntfy
package_name: ntfy
homepage: https://heckel.io/ntfy homepage: https://heckel.io/ntfy
maintainer: Philipp C. Heckel <philipp.heckel@gmail.com> maintainer: Philipp C. Heckel <philipp.heckel@gmail.com>
description: Simple pub-sub notification service description: Simple pub-sub notification service
@@ -106,9 +100,8 @@ nfpms:
preremove: "scripts/prerm.sh" preremove: "scripts/prerm.sh"
postremove: "scripts/postrm.sh" postremove: "scripts/postrm.sh"
archives: archives:
- - id: ntfy_linux
id: ntfy_linux ids:
builds:
- ntfy_linux_amd64 - ntfy_linux_amd64
- ntfy_linux_armv6 - ntfy_linux_armv6
- ntfy_linux_armv7 - ntfy_linux_armv7
@@ -122,19 +115,17 @@ archives:
- client/client.yml - client/client.yml
- client/ntfy-client.service - client/ntfy-client.service
- client/user/ntfy-client.service - client/user/ntfy-client.service
- - id: ntfy_windows
id: ntfy_windows ids:
builds:
- ntfy_windows_amd64 - ntfy_windows_amd64
format: zip formats: [ zip ]
wrap_in_directory: true wrap_in_directory: true
files: files:
- LICENSE - LICENSE
- README.md - README.md
- client/client.yml - client/client.yml
- - id: ntfy_darwin
id: ntfy_darwin ids:
builds:
- ntfy_darwin_all - ntfy_darwin_all
wrap_in_directory: true wrap_in_directory: true
files: files:
@@ -142,14 +133,13 @@ archives:
- README.md - README.md
- client/client.yml - client/client.yml
universal_binaries: universal_binaries:
- - id: ntfy_darwin_all
id: ntfy_darwin_all
replace: true replace: true
name_template: ntfy name_template: ntfy
checksum: checksum:
name_template: 'checksums.txt' name_template: 'checksums.txt'
snapshot: snapshot:
name_template: "{{ .Tag }}-next" version_template: "{{ .Tag }}-next"
changelog: changelog:
sort: asc sort: asc
filters: filters:

View File

@@ -220,7 +220,7 @@ cli-deps-static-sites:
touch server/docs/index.html server/site/app.html touch server/docs/index.html server/site/app.html
cli-deps-all: cli-deps-all:
go install github.com/goreleaser/goreleaser@latest go install github.com/goreleaser/goreleaser/v2@latest
cli-deps-gcc-armv6-armv7: cli-deps-gcc-armv6-armv7:
which arm-linux-gnueabi-gcc || { echo "ERROR: ARMv6/ARMv7 cross compiler not installed. On Ubuntu, run: apt install gcc-arm-linux-gnueabi"; exit 1; } which arm-linux-gnueabi-gcc || { echo "ERROR: ARMv6/ARMv7 cross compiler not installed. On Ubuntu, run: apt install gcc-arm-linux-gnueabi"; exit 1; }

View File

@@ -52,7 +52,7 @@ var flagsServe = append(
altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-file", Aliases: []string{"auth_file", "H"}, EnvVars: []string{"NTFY_AUTH_FILE"}, Usage: "auth database file used for access control"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-file", Aliases: []string{"auth_file", "H"}, EnvVars: []string{"NTFY_AUTH_FILE"}, Usage: "auth database file used for access control"}),
altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-startup-queries", Aliases: []string{"auth_startup_queries"}, EnvVars: []string{"NTFY_AUTH_STARTUP_QUERIES"}, Usage: "queries run when the auth database is initialized"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-startup-queries", Aliases: []string{"auth_startup_queries"}, EnvVars: []string{"NTFY_AUTH_STARTUP_QUERIES"}, Usage: "queries run when the auth database is initialized"}),
altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-default-access", Aliases: []string{"auth_default_access", "p"}, EnvVars: []string{"NTFY_AUTH_DEFAULT_ACCESS"}, Value: "read-write", Usage: "default permissions if no matching entries in the auth database are found"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-default-access", Aliases: []string{"auth_default_access", "p"}, EnvVars: []string{"NTFY_AUTH_DEFAULT_ACCESS"}, Value: "read-write", Usage: "default permissions if no matching entries in the auth database are found"}),
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{Name: "auth-users", Aliases: []string{"auth_users"}, EnvVars: []string{"NTFY_AUTH_USERS"}, Usage: "pre-provisioned declarative users"}), altsrc.NewStringSliceFlag(&cli.StringSliceFlag{Name: "auth-provisioned-users", Aliases: []string{"auth_provisioned_users"}, EnvVars: []string{"NTFY_AUTH_PROVISIONED_USERS"}, Usage: "pre-provisioned declarative users"}),
altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-cache-dir", Aliases: []string{"attachment_cache_dir"}, EnvVars: []string{"NTFY_ATTACHMENT_CACHE_DIR"}, Usage: "cache directory for attached files"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-cache-dir", Aliases: []string{"attachment_cache_dir"}, EnvVars: []string{"NTFY_ATTACHMENT_CACHE_DIR"}, Usage: "cache directory for attached files"}),
altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-total-size-limit", Aliases: []string{"attachment_total_size_limit", "A"}, EnvVars: []string{"NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT"}, Value: util.FormatSize(server.DefaultAttachmentTotalSizeLimit), Usage: "limit of the on-disk attachment cache"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-total-size-limit", Aliases: []string{"attachment_total_size_limit", "A"}, EnvVars: []string{"NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT"}, Value: util.FormatSize(server.DefaultAttachmentTotalSizeLimit), Usage: "limit of the on-disk attachment cache"}),
altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-file-size-limit", Aliases: []string{"attachment_file_size_limit", "Y"}, EnvVars: []string{"NTFY_ATTACHMENT_FILE_SIZE_LIMIT"}, Value: util.FormatSize(server.DefaultAttachmentFileSizeLimit), Usage: "per-file attachment size limit (e.g. 300k, 2M, 100M)"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-file-size-limit", Aliases: []string{"attachment_file_size_limit", "Y"}, EnvVars: []string{"NTFY_ATTACHMENT_FILE_SIZE_LIMIT"}, Value: util.FormatSize(server.DefaultAttachmentFileSizeLimit), Usage: "per-file attachment size limit (e.g. 300k, 2M, 100M)"}),
@@ -158,7 +158,8 @@ func execServe(c *cli.Context) error {
authFile := c.String("auth-file") authFile := c.String("auth-file")
authStartupQueries := c.String("auth-startup-queries") authStartupQueries := c.String("auth-startup-queries")
authDefaultAccess := c.String("auth-default-access") authDefaultAccess := c.String("auth-default-access")
authUsers := c.StringSlice("auth-users") authProvisionedUsersRaw := c.StringSlice("auth-provisioned-users")
//authProvisionedAccessRaw := c.StringSlice("auth-provisioned-access")
attachmentCacheDir := c.String("attachment-cache-dir") attachmentCacheDir := c.String("attachment-cache-dir")
attachmentTotalSizeLimitStr := c.String("attachment-total-size-limit") attachmentTotalSizeLimitStr := c.String("attachment-total-size-limit")
attachmentFileSizeLimitStr := c.String("attachment-file-size-limit") attachmentFileSizeLimitStr := c.String("attachment-file-size-limit")
@@ -348,11 +349,33 @@ func execServe(c *cli.Context) error {
webRoot = "/" + webRoot webRoot = "/" + webRoot
} }
// Default auth permissions // Convert default auth permission, read provisioned users
authDefault, err := user.ParsePermission(authDefaultAccess) authDefault, err := user.ParsePermission(authDefaultAccess)
if err != nil { if err != nil {
return errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'") return errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'")
} }
authProvisionedUsers := make([]*user.User, 0)
for _, userLine := range authProvisionedUsersRaw {
parts := strings.Split(userLine, ":")
if len(parts) != 3 {
return fmt.Errorf("invalid provisioned user %s, expected format: 'name:hash:role'", userLine)
}
username := strings.TrimSpace(parts[0])
passwordHash := strings.TrimSpace(parts[1])
role := user.Role(strings.TrimSpace(parts[2]))
if !user.AllowedUsername(username) {
return fmt.Errorf("invalid provisioned user %s, username invalid", userLine)
} else if passwordHash == "" {
return fmt.Errorf("invalid provisioned user %s, password hash cannot be empty", userLine)
} else if !user.AllowedRole(role) {
return fmt.Errorf("invalid provisioned user %s, role %s is not allowed, allowed roles are 'admin' or 'user'", userLine, role)
}
authProvisionedUsers = append(authProvisionedUsers, &user.User{
Name: username,
Hash: passwordHash,
Role: role,
})
}
// Special case: Unset default // Special case: Unset default
if listenHTTP == "-" { if listenHTTP == "-" {
@@ -408,7 +431,8 @@ func execServe(c *cli.Context) error {
conf.AuthFile = authFile conf.AuthFile = authFile
conf.AuthStartupQueries = authStartupQueries conf.AuthStartupQueries = authStartupQueries
conf.AuthDefault = authDefault conf.AuthDefault = authDefault
conf.AuthUsers = nil // FIXME conf.AuthProvisionedUsers = authProvisionedUsers
conf.AuthProvisionedAccess = nil // FIXME
conf.AttachmentCacheDir = attachmentCacheDir conf.AttachmentCacheDir = attachmentCacheDir
conf.AttachmentTotalSizeLimit = attachmentTotalSizeLimit conf.AttachmentTotalSizeLimit = attachmentTotalSizeLimit
conf.AttachmentFileSizeLimit = attachmentFileSizeLimit conf.AttachmentFileSizeLimit = attachmentFileSizeLimit

View File

@@ -224,7 +224,7 @@ func execUserDel(c *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
if _, err := manager.User(username); err == user.ErrUserNotFound { if _, err := manager.User(username); errors.Is(err, user.ErrUserNotFound) {
return fmt.Errorf("user %s does not exist", username) return fmt.Errorf("user %s does not exist", username)
} }
if err := manager.RemoveUser(username); err != nil { if err := manager.RemoveUser(username); err != nil {
@@ -250,7 +250,7 @@ func execUserChangePass(c *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
if _, err := manager.User(username); err == user.ErrUserNotFound { if _, err := manager.User(username); errors.Is(err, user.ErrUserNotFound) {
return fmt.Errorf("user %s does not exist", username) return fmt.Errorf("user %s does not exist", username)
} }
if password == "" { if password == "" {
@@ -278,7 +278,7 @@ func execUserChangeRole(c *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
if _, err := manager.User(username); err == user.ErrUserNotFound { if _, err := manager.User(username); errors.Is(err, user.ErrUserNotFound) {
return fmt.Errorf("user %s does not exist", username) return fmt.Errorf("user %s does not exist", username)
} }
if err := manager.ChangeRole(username, role); err != nil { if err := manager.ChangeRole(username, role); err != nil {
@@ -302,7 +302,7 @@ func execUserChangeTier(c *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
if _, err := manager.User(username); err == user.ErrUserNotFound { if _, err := manager.User(username); errors.Is(err, user.ErrUserNotFound) {
return fmt.Errorf("user %s does not exist", username) return fmt.Errorf("user %s does not exist", username)
} }
if tier == tierReset { if tier == tierReset {
@@ -344,7 +344,16 @@ func createUserManager(c *cli.Context) (*user.Manager, error) {
if err != nil { if err != nil {
return nil, errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'") return nil, errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'")
} }
return user.NewManager(authFile, authStartupQueries, authDefault, user.DefaultUserPasswordBcryptCost, user.DefaultUserStatsQueueWriterInterval) authConfig := &user.Config{
Filename: authFile,
StartupQueries: authStartupQueries,
DefaultAccess: authDefault,
ProvisionedUsers: nil, //FIXME
ProvisionedAccess: nil, //FIXME
BcryptCost: user.DefaultUserPasswordBcryptCost,
QueueWriterInterval: user.DefaultUserStatsQueueWriterInterval,
}
return user.NewManager(authConfig)
} }
func readPasswordAndConfirm(c *cli.Context) (string, error) { func readPasswordAndConfirm(c *cli.Context) (string, error) {

View File

@@ -93,7 +93,8 @@ type Config struct {
AuthFile string AuthFile string
AuthStartupQueries string AuthStartupQueries string
AuthDefault user.Permission AuthDefault user.Permission
AuthUsers []user.User AuthProvisionedUsers []*user.User
AuthProvisionedAccess map[string][]*user.Grant
AuthBcryptCost int AuthBcryptCost int
AuthStatsQueueWriterInterval time.Duration AuthStatsQueueWriterInterval time.Duration
AttachmentCacheDir string AttachmentCacheDir string

View File

@@ -193,6 +193,8 @@ func New(conf *Config) (*Server, error) {
Filename: conf.AuthFile, Filename: conf.AuthFile,
StartupQueries: conf.AuthStartupQueries, StartupQueries: conf.AuthStartupQueries,
DefaultAccess: conf.AuthDefault, DefaultAccess: conf.AuthDefault,
ProvisionedUsers: conf.AuthProvisionedUsers,
ProvisionedAccess: conf.AuthProvisionedAccess,
BcryptCost: conf.AuthBcryptCost, BcryptCost: conf.AuthBcryptCost,
QueueWriterInterval: conf.AuthStatsQueueWriterInterval, QueueWriterInterval: conf.AuthStatsQueueWriterInterval,
} }

View File

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