Add simple file-based auth system by anon5

master
HimbeerserverDE 2021-09-13 15:54:57 +02:00
parent c978ee8e27
commit 8ddb6cc919
No known key found for this signature in database
GPG Key ID: 1A651504791E6A8B
7 changed files with 230 additions and 39 deletions

193
auth_files.go Normal file
View File

@ -0,0 +1,193 @@
package proxy
import (
"net"
"os"
"time"
)
type authFiles struct{}
// Exists reports whether a user is registered.
func (a authFiles) Exists(name string) bool {
os.Mkdir(Path("auth"), 0700)
_, err := os.Stat(Path("auth/", name))
return err == nil
}
// Passwd returns the SRP salt and verifier of a user or an error.
func (a authFiles) Passwd(name string) (salt, verifier []byte, err error) {
os.Mkdir(Path("auth"), 0700)
salt, err = os.ReadFile(Path("auth/", name, "/salt"))
if err != nil {
return
}
verifier, err = os.ReadFile(Path("auth/", name, "/verifier"))
if err != nil {
return
}
a.updateTimestamp(name)
return
}
// SetPasswd creates a password entry if necessary
// and sets the password of a user.
func (a authFiles) SetPasswd(name string, salt, verifier []byte) error {
os.Mkdir(Path("auth"), 0700)
os.Mkdir(Path("auth/", name), 0700)
if err := os.WriteFile(Path("auth/", name, "/salt"), salt, 0600); err != nil {
return err
}
if err := os.WriteFile(Path("auth/", name, "/verifier"), verifier, 0600); err != nil {
return err
}
a.updateTimestamp(name)
return nil
}
// Timestamp returns the last time an authentication entry was accessed
// or an error.
func (a authFiles) Timestamp(name string) (time.Time, error) {
os.Mkdir(Path("auth"), 0700)
info, err := os.Stat(Path("auth/", name, "/timestamp"))
if err != nil {
return time.Time{}, err
}
return info.ModTime(), nil
}
// Import deletes all users and adds the passed users.
func (a authFiles) Import(in []user) {
os.Mkdir(Path("auth"), 0700)
for _, u := range in {
a.SetPasswd(u.name, u.salt, u.verifier)
os.Chtimes(Path("auth/", u.name, "/timestamp"), u.timestamp, u.timestamp)
}
}
// Export returns data that can be processed by Import
// or an error.
func (a authFiles) Export() ([]user, error) {
dir, err := os.ReadDir(Path("auth"))
if err != nil {
return nil, err
}
var out []user
for _, f := range dir {
u := user{name: f.Name()}
u.timestamp, err = a.Timestamp(u.name)
if err != nil {
return nil, err
}
u.salt, u.verifier, err = a.Passwd(u.name)
if err != nil {
return nil, err
}
out = append(out, u)
}
return out, nil
}
// Ban adds a ban entry for a network address and an associated name.
func (a authFiles) Ban(addr, name string) error {
os.Mkdir(Path("ban"), 0700)
return os.WriteFile(Path("ban/", addr), []byte(name), 0600)
}
// Unban deletes a ban entry. It accepts both network addresses
// and player names.
func (a authFiles) Unban(id string) error {
os.Mkdir(Path("ban"), 0700)
if err := os.Remove(Path("ban/", id)); err != nil {
if os.IsNotExist(err) {
dir, err := os.ReadDir(Path("ban"))
if err != nil {
return err
}
for _, f := range dir {
name, err := os.ReadFile(Path("ban/", f.Name()))
if err != nil {
return err
}
if string(name) == id {
return os.Remove(Path("ban/", f.Name()))
break
}
}
}
}
return nil
}
// Banned reports whether a network address is banned.
func (a authFiles) Banned(addr *net.UDPAddr) bool {
os.Mkdir(Path("ban"), 0700)
_, err := os.Stat(Path("ban/", addr.IP.String()))
if os.IsNotExist(err) {
return false
}
return true
}
// ImportBans deletes all ban entries and adds the passed entries.
func (a authFiles) ImportBans(in []ban) {
os.Mkdir(Path("ban"), 0700)
for _, b := range in {
a.Ban(b.addr, b.name)
}
}
// ExportBans returns data that can be processed by ImportBans
// or an error,
func (a authFiles) ExportBans() ([]ban, error) {
os.Mkdir(Path("ban"), 0700)
dir, err := os.ReadDir(Path("ban"))
if err != nil {
return nil, err
}
var out []ban
for _, f := range dir {
b := ban{addr: f.Name()}
name, err := os.ReadFile(Path("ban/", f.Name()))
if err != nil {
return nil, err
}
b.name = string(name)
out = append(out, b)
}
return out, nil
}
func (a authFiles) updateTimestamp(name string) {
os.Mkdir(Path("auth"), 0700)
t := time.Now().Local()
os.Chtimes(Path("auth/", name, "/timestamp"), t, t)
}

View File

@ -4,8 +4,6 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"net" "net"
"os"
"path/filepath"
"time" "time"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@ -73,8 +71,7 @@ func (a authSQLite3) Timestamp(name string) (time.Time, error) {
return time.Parse("2006-01-02 15:04:05", tstr) return time.Parse("2006-01-02 15:04:05", tstr)
} }
// Import deletes all users and and adds the passed // Import deletes all users and adds the passed users.
// users.
func (a authSQLite3) Import(in []user) { func (a authSQLite3) Import(in []user) {
if err := a.init(); err != nil { if err := a.init(); err != nil {
return return
@ -114,13 +111,14 @@ func (a authSQLite3) Export() ([]user, error) {
var out []user var out []user
for _, name := range names { for _, name := range names {
var u user u := user{name: name}
u.timestamp, err = a.Timestamp(name)
u.timestamp, err = a.Timestamp(u.name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
u.salt, u.verifier, err = a.Passwd(name) u.salt, u.verifier, err = a.Passwd(u.name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -224,13 +222,8 @@ func (a authSQLite3) updateTimestamp(name string) {
} }
func (a *authSQLite3) init() error { func (a *authSQLite3) init() error {
executable, err := os.Executable() var err error
if err != nil { a.db, err = sql.Open("sqlite3", Path("auth.sqlite3"))
return err
}
path := filepath.Dir(executable) + "/auth.sqlite"
a.db, err = sql.Open("sqlite3", path)
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"log" "log"
"os" "os"
"path/filepath"
"sync" "sync"
) )
@ -12,7 +11,7 @@ const (
defaultCmdPrefix = ">" defaultCmdPrefix = ">"
defaultSendInterval = 0.09 defaultSendInterval = 0.09
defaultUserLimit = 10 defaultUserLimit = 10
defaultAuthBackend = "sqlite3" defaultAuthBackend = "files"
defaultTelnetAddr = "[::1]:40010" defaultTelnetAddr = "[::1]:40010"
defaultBindAddr = ":40000" defaultBindAddr = ":40000"
defaultListInterval = 300 defaultListInterval = 300
@ -93,13 +92,7 @@ func LoadConfig() error {
config.UserGroups = make(map[string]string) config.UserGroups = make(map[string]string)
config.List.Interval = defaultListInterval config.List.Interval = defaultListInterval
executable, err := os.Executable() f, err := os.OpenFile(Path("config.json"), os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return err
}
path := filepath.Dir(executable) + "/config.json"
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)
if err != nil { if err != nil {
config = oldConf config = oldConf
return err return err

9
log.go
View File

@ -3,7 +3,6 @@ package proxy
import ( import (
"log" "log"
"os" "os"
"path/filepath"
) )
var logWriter *LogWriter var logWriter *LogWriter
@ -27,13 +26,7 @@ func init() {
log.SetPrefix("[proxy] ") log.SetPrefix("[proxy] ")
log.SetFlags(log.Flags() | log.Lmsgprefix) log.SetFlags(log.Flags() | log.Lmsgprefix)
executable, err := os.Executable() f, err := os.OpenFile(Path("latest.log"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
log.Fatal(err)
}
path := filepath.Dir(executable) + "/latest.log"
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -3,7 +3,6 @@ package proxy
import ( import (
"log" "log"
"os" "os"
"path/filepath"
"plugin" "plugin"
"sync" "sync"
) )
@ -15,12 +14,7 @@ func loadPlugins() {
} }
func openPlugins() { func openPlugins() {
executable, err := os.Executable() path := Path("plugins")
if err != nil {
log.Fatal(err)
}
path := filepath.Dir(executable) + "/plugins"
os.Mkdir(path, 0777) os.Mkdir(path, 0777)
dir, err := os.ReadDir(path) dir, err := os.ReadDir(path)

View File

@ -4,7 +4,14 @@ It also provides an API for plugins.
*/ */
package proxy package proxy
import "regexp" import (
"log"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
)
const ( const (
latestSerializeVer = 28 latestSerializeVer = 28
@ -15,3 +22,19 @@ const (
) )
var playerNameChars = regexp.MustCompile("^[a-zA-Z0-9-_]+$") var playerNameChars = regexp.MustCompile("^[a-zA-Z0-9-_]+$")
var proxyDir string
var proxyDirOnce sync.Once
func Path(path ...string) string {
proxyDirOnce.Do(func() {
executable, err := os.Executable()
if err != nil {
log.Fatal(err)
}
proxyDir = filepath.Dir(executable)
})
return proxyDir + "/" + strings.Join(path, "")
}

2
run.go
View File

@ -25,6 +25,8 @@ func Run() {
switch Conf().AuthBackend { switch Conf().AuthBackend {
case "sqlite3": case "sqlite3":
setAuthBackend(authSQLite3{}) setAuthBackend(authSQLite3{})
case "files":
setAuthBackend(authFiles{})
default: default:
log.Fatal("invalid auth backend") log.Fatal("invalid auth backend")
} }