Add simple file-based auth system by anon5
parent
c978ee8e27
commit
8ddb6cc919
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
11
config.go
11
config.go
|
@ -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
9
log.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
25
proxy.go
25
proxy.go
|
@ -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
2
run.go
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue