allow multiple users to be set in object provider.

This commit is contained in:
Miroslav Šedivý 2023-01-13 00:39:30 +01:00
parent 107eba22a5
commit 1fb5ae43dd
5 changed files with 77 additions and 66 deletions

View File

@ -65,11 +65,41 @@ desktop:
member: member:
provider: "object" provider: "object"
object: object:
admin_password: "admin" users:
user_password: "neko" - username: "admin"
password: "admin"
profile:
name: "Administrator"
is_admin: true
can_login: true
can_connect: true
can_watch: true
can_host: true
can_share_media: true
can_access_clipboard: true
sends_inactive_cursor: true
can_see_inactive_cursors: true
- username: "user"
password: "neko"
profile:
name: "User"
is_admin: false
can_login: true
can_connect: true
can_watch: true
can_host: true
can_share_media: true
can_access_clipboard: true
sends_inactive_cursor: true
can_see_inactive_cursors: false
# provider: "file" # provider: "file"
# file: # file:
# path: "/home/neko/members.json" # path: "/home/neko/members.json"
# provider: "multiuser"
# multiuser:
# admin_password: "admin"
# user_password: "neko"
# provider: "noauth"
session: session:
# Allows reconnecting the websocket even if the previous # Allows reconnecting the websocket even if the previous

View File

@ -1,12 +1,14 @@
package config package config
import ( import (
"github.com/rs/zerolog/log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/demodesk/neko/internal/member/file" "github.com/demodesk/neko/internal/member/file"
"github.com/demodesk/neko/internal/member/multiuser" "github.com/demodesk/neko/internal/member/multiuser"
"github.com/demodesk/neko/internal/member/object" "github.com/demodesk/neko/internal/member/object"
"github.com/demodesk/neko/pkg/utils"
) )
type Member struct { type Member struct {
@ -31,13 +33,8 @@ func (Member) Init(cmd *cobra.Command) error {
} }
// object provider // object provider
cmd.PersistentFlags().String("member.object.user_password", "", "member object provider: user password") cmd.PersistentFlags().String("member.object.users", "[]", "member object provider: users in JSON format")
if err := viper.BindPFlag("member.object.user_password", cmd.PersistentFlags().Lookup("member.object.user_password")); err != nil { if err := viper.BindPFlag("member.object.users", cmd.PersistentFlags().Lookup("member.object.users")); err != nil {
return err
}
cmd.PersistentFlags().String("member.object.admin_password", "", "member object provider: admin password")
if err := viper.BindPFlag("member.object.admin_password", cmd.PersistentFlags().Lookup("member.object.admin_password")); err != nil {
return err return err
} }
@ -62,8 +59,11 @@ func (s *Member) Set() {
s.File.Path = viper.GetString("member.file.path") s.File.Path = viper.GetString("member.file.path")
// object provider // object provider
s.Object.UserPassword = viper.GetString("member.object.user_password") if err := viper.UnmarshalKey("member.object.users", &s.Object.Users, viper.DecodeHook(
s.Object.AdminPassword = viper.GetString("member.object.admin_password") utils.JsonStringAutoDecode(s.Object.Users),
)); err != nil {
log.Warn().Err(err).Msgf("unable to parse member object users")
}
// multiuser provider // multiuser provider
s.Multiuser.UserPassword = viper.GetString("member.multiuser.user_password") s.Multiuser.UserPassword = viper.GetString("member.multiuser.user_password")

View File

@ -7,48 +7,20 @@ import (
func New(config Config) types.MemberProvider { func New(config Config) types.MemberProvider {
return &MemberProviderCtx{ return &MemberProviderCtx{
config: config, config: config,
entries: make(map[string]*MemberEntry), entries: make(map[string]*memberEntry),
} }
} }
type MemberProviderCtx struct { type MemberProviderCtx struct {
config Config config Config
entries map[string]*MemberEntry entries map[string]*memberEntry
} }
func (provider *MemberProviderCtx) Connect() error { func (provider *MemberProviderCtx) Connect() error {
var err error var err error
if provider.config.AdminPassword != "" { for _, entry := range provider.config.Users {
// create default admin account at startup _, err = provider.Insert(entry.Username, entry.Password, entry.Profile)
_, err = provider.Insert("admin", provider.config.AdminPassword, types.MemberProfile{
Name: "Administrator",
IsAdmin: true,
CanLogin: true,
CanConnect: true,
CanWatch: true,
CanHost: true,
CanShareMedia: true,
CanAccessClipboard: true,
SendsInactiveCursor: true,
CanSeeInactiveCursors: true,
})
}
if provider.config.UserPassword != "" {
// create default user account at startup
_, err = provider.Insert("user", provider.config.UserPassword, types.MemberProfile{
Name: "User",
IsAdmin: false,
CanLogin: true,
CanConnect: true,
CanWatch: true,
CanHost: true,
CanShareMedia: true,
CanAccessClipboard: true,
SendsInactiveCursor: true,
CanSeeInactiveCursors: false,
})
} }
return err return err
@ -68,11 +40,11 @@ func (provider *MemberProviderCtx) Authenticate(username string, password string
} }
// TODO: Use hash function. // TODO: Use hash function.
if entry.Password != password { if !entry.CheckPassword(password) {
return "", types.MemberProfile{}, types.ErrMemberInvalidPassword return "", types.MemberProfile{}, types.ErrMemberInvalidPassword
} }
return id, entry.Profile, nil return id, entry.profile, nil
} }
func (provider *MemberProviderCtx) Insert(username string, password string, profile types.MemberProfile) (string, error) { func (provider *MemberProviderCtx) Insert(username string, password string, profile types.MemberProfile) (string, error) {
@ -84,10 +56,10 @@ func (provider *MemberProviderCtx) Insert(username string, password string, prof
return "", types.ErrMemberAlreadyExists return "", types.ErrMemberAlreadyExists
} }
provider.entries[id] = &MemberEntry{ provider.entries[id] = &memberEntry{
// TODO: Use hash function. // TODO: Use hash function.
Password: password, password: password,
Profile: profile, profile: profile,
} }
return id, nil return id, nil
@ -99,7 +71,7 @@ func (provider *MemberProviderCtx) UpdateProfile(id string, profile types.Member
return types.ErrMemberDoesNotExist return types.ErrMemberDoesNotExist
} }
entry.Profile = profile entry.profile = profile
return nil return nil
} }
@ -111,7 +83,7 @@ func (provider *MemberProviderCtx) UpdatePassword(id string, password string) er
} }
// TODO: Use hash function. // TODO: Use hash function.
entry.Password = password entry.password = password
return nil return nil
} }
@ -122,7 +94,7 @@ func (provider *MemberProviderCtx) Select(id string) (types.MemberProfile, error
return types.MemberProfile{}, types.ErrMemberDoesNotExist return types.MemberProfile{}, types.ErrMemberDoesNotExist
} }
return entry.Profile, nil return entry.profile, nil
} }
func (provider *MemberProviderCtx) SelectAll(limit int, offset int) (map[string]types.MemberProfile, error) { func (provider *MemberProviderCtx) SelectAll(limit int, offset int) (map[string]types.MemberProfile, error) {
@ -131,7 +103,7 @@ func (provider *MemberProviderCtx) SelectAll(limit int, offset int) (map[string]
i := 0 i := 0
for id, entry := range provider.entries { for id, entry := range provider.entries {
if i >= offset && (limit == 0 || i < offset+limit) { if i >= offset && (limit == 0 || i < offset+limit) {
profiles[id] = entry.Profile profiles[id] = entry.profile
} }
i = i + 1 i = i + 1

View File

@ -4,12 +4,21 @@ import (
"github.com/demodesk/neko/pkg/types" "github.com/demodesk/neko/pkg/types"
) )
type MemberEntry struct { type memberEntry struct {
Password string `json:"password"` password string
Profile types.MemberProfile `json:"profile"` profile types.MemberProfile
}
func (m *memberEntry) CheckPassword(password string) bool {
return m.password == password
}
type User struct {
Username string
Password string
Profile types.MemberProfile
} }
type Config struct { type Config struct {
AdminPassword string Users []User
UserPassword string
} }

View File

@ -12,15 +12,15 @@ type MemberProfile struct {
Name string `json:"name"` Name string `json:"name"`
// permissions // permissions
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin" mapstructure:"is_admin"`
CanLogin bool `json:"can_login"` CanLogin bool `json:"can_login" mapstructure:"can_login"`
CanConnect bool `json:"can_connect"` CanConnect bool `json:"can_connect" mapstructure:"can_connect"`
CanWatch bool `json:"can_watch"` CanWatch bool `json:"can_watch" mapstructure:"can_watch"`
CanHost bool `json:"can_host"` CanHost bool `json:"can_host" mapstructure:"can_host"`
CanShareMedia bool `json:"can_share_media"` CanShareMedia bool `json:"can_share_media" mapstructure:"can_share_media"`
CanAccessClipboard bool `json:"can_access_clipboard"` CanAccessClipboard bool `json:"can_access_clipboard" mapstructure:"can_access_clipboard"`
SendsInactiveCursor bool `json:"sends_inactive_cursor"` SendsInactiveCursor bool `json:"sends_inactive_cursor" mapstructure:"sends_inactive_cursor"`
CanSeeInactiveCursors bool `json:"can_see_inactive_cursors"` CanSeeInactiveCursors bool `json:"can_see_inactive_cursors" mapstructure:"can_see_inactive_cursors"`
// plugin scope // plugin scope
Plugins map[string]any `json:"plugins"` Plugins map[string]any `json:"plugins"`