effba3b690
Add godoc comments for all 62 exported symbols across 20 files in config/parsing/: 23 MDKey constants, 35 Parse*/List/Default* functions, 3 TLS helpers, 1 Args struct with 7 fields, and the package doc.
185 lines
4.6 KiB
Go
185 lines
4.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/tls"
|
|
"io"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/go-gost/core/auth"
|
|
"github.com/go-gost/core/logger"
|
|
xauth "github.com/go-gost/x/auth"
|
|
auth_plugin "github.com/go-gost/x/auth/plugin"
|
|
"github.com/go-gost/x/config"
|
|
"github.com/go-gost/x/internal/loader"
|
|
"github.com/go-gost/x/internal/plugin"
|
|
"github.com/go-gost/x/registry"
|
|
)
|
|
|
|
// ParseAuther converts an AutherConfig into an auth.Authenticator. It handles
|
|
// plugin backends (HTTP or gRPC), inline auth credentials, and optional file,
|
|
// Redis, and HTTP hot-reload sources.
|
|
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
|
|
if cfg == nil {
|
|
return nil
|
|
}
|
|
|
|
if cfg.Plugin != nil {
|
|
var tlsCfg *tls.Config
|
|
if cfg.Plugin.TLS != nil {
|
|
tlsCfg = &tls.Config{
|
|
ServerName: cfg.Plugin.TLS.ServerName,
|
|
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
|
|
}
|
|
}
|
|
switch cfg.Plugin.Type {
|
|
case "http":
|
|
return auth_plugin.NewHTTPPlugin(
|
|
cfg.Name, cfg.Plugin.Addr,
|
|
plugin.TLSConfigOption(tlsCfg),
|
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
|
)
|
|
default:
|
|
return auth_plugin.NewGRPCPlugin(
|
|
cfg.Name, cfg.Plugin.Addr,
|
|
plugin.TokenOption(cfg.Plugin.Token),
|
|
plugin.TLSConfigOption(tlsCfg),
|
|
)
|
|
}
|
|
}
|
|
|
|
m := make(map[string]string)
|
|
|
|
for _, user := range cfg.Auths {
|
|
if user.Username == "" {
|
|
continue
|
|
}
|
|
m[user.Username] = user.Password
|
|
}
|
|
|
|
opts := []xauth.Option{
|
|
xauth.AuthsOption(m),
|
|
xauth.ReloadPeriodOption(cfg.Reload),
|
|
xauth.LoggerOption(logger.Default().WithFields(map[string]any{
|
|
"kind": "auther",
|
|
"auther": cfg.Name,
|
|
})),
|
|
}
|
|
if cfg.File != nil && cfg.File.Path != "" {
|
|
opts = append(opts, xauth.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
|
|
}
|
|
if cfg.Redis != nil && cfg.Redis.Addr != "" {
|
|
opts = append(opts, xauth.RedisLoaderOption(loader.RedisHashLoader(
|
|
cfg.Redis.Addr,
|
|
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
|
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
|
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
|
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
|
)))
|
|
}
|
|
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
|
|
opts = append(opts, xauth.HTTPLoaderOption(loader.HTTPLoader(
|
|
cfg.HTTP.URL,
|
|
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
|
|
)))
|
|
}
|
|
return xauth.NewAuthenticator(opts...)
|
|
}
|
|
|
|
// ParseAutherFromAuth creates a simple authenticator from a single
|
|
// username/password pair in an AuthConfig. Returns nil if the config is nil or
|
|
// has no username.
|
|
func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
|
|
if au == nil || au.Username == "" {
|
|
return nil
|
|
}
|
|
return xauth.NewAuthenticator(
|
|
xauth.AuthsOption(
|
|
map[string]string{
|
|
au.Username: au.Password,
|
|
},
|
|
),
|
|
xauth.LoggerOption(logger.Default().WithFields(map[string]any{
|
|
"kind": "auther",
|
|
})),
|
|
)
|
|
}
|
|
|
|
// Info extracts authentication credentials from an AuthConfig as a
|
|
// url.Userinfo. It supports reading the first line from a password file when
|
|
// cfg.File is set, or falls back to inline username/password fields.
|
|
func Info(cfg *config.AuthConfig) *url.Userinfo {
|
|
if cfg == nil {
|
|
return nil
|
|
}
|
|
|
|
if cfg.File != "" {
|
|
if f, _ := os.Open(cfg.File); f != nil {
|
|
defer f.Close()
|
|
if infos, _ := parseInfo(f, 1); len(infos) > 0 {
|
|
return infos[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
if cfg.Username == "" {
|
|
return nil
|
|
}
|
|
|
|
if cfg.Password == "" {
|
|
return url.User(cfg.Username)
|
|
}
|
|
return url.UserPassword(cfg.Username, cfg.Password)
|
|
}
|
|
|
|
func parseInfo(r io.Reader, max int) (infos []*url.Userinfo, err error) {
|
|
if r == nil {
|
|
return
|
|
}
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
// line := strings.Replace(scanner.Text(), "\t", " ", -1)
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if n := strings.IndexByte(line, '#'); n == 0 {
|
|
continue
|
|
}
|
|
sp := strings.SplitN(line, " ", 2)
|
|
if len(sp) == 1 {
|
|
if k := strings.TrimSpace(sp[0]); k != "" {
|
|
infos = append(infos, url.User(k))
|
|
}
|
|
}
|
|
if len(sp) == 2 {
|
|
if k := strings.TrimSpace(sp[0]); k != "" {
|
|
infos = append(infos, url.UserPassword(k, strings.TrimSpace(sp[1])))
|
|
}
|
|
}
|
|
|
|
if max > 0 && len(infos) >= max {
|
|
break
|
|
}
|
|
}
|
|
|
|
err = scanner.Err()
|
|
return
|
|
}
|
|
|
|
// List resolves one or more authenticator names from the registry. It returns
|
|
// only the authenticators that were found, skipping any that are not
|
|
// registered.
|
|
func List(name string, names ...string) []auth.Authenticator {
|
|
var authers []auth.Authenticator
|
|
if auther := registry.AutherRegistry().Get(name); auther != nil {
|
|
authers = append(authers, auther)
|
|
}
|
|
for _, s := range names {
|
|
if auther := registry.AutherRegistry().Get(s); auther != nil {
|
|
authers = append(authers, auther)
|
|
}
|
|
}
|
|
return authers
|
|
}
|