Add infrastructure for external authentication
This commit is contained in:
parent
d67e59658d
commit
63ca247354
21
auth/auth.go
Normal file
21
auth/auth.go
Normal file
@ -0,0 +1,21 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
)
|
||||
|
||||
type PlainAuthenticator interface {
|
||||
AuthPlain(ctx context.Context, db database.Database, username, password string) error
|
||||
}
|
||||
|
||||
func New(driver, source string) (PlainAuthenticator, error) {
|
||||
switch driver {
|
||||
case "internal":
|
||||
return NewInternal(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown auth driver %q", driver)
|
||||
}
|
||||
}
|
34
auth/internal.go
Normal file
34
auth/internal.go
Normal file
@ -0,0 +1,34 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
)
|
||||
|
||||
type internal struct{}
|
||||
|
||||
func NewInternal() PlainAuthenticator {
|
||||
return internal{}
|
||||
}
|
||||
|
||||
func (internal) AuthPlain(ctx context.Context, db database.Database, username, password string) error {
|
||||
u, err := db.GetUser(ctx, username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
upgraded, err := u.CheckPassword(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if upgraded {
|
||||
if err := db.StoreUser(ctx, u); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
"git.sr.ht/~emersion/soju"
|
||||
"git.sr.ht/~emersion/soju/auth"
|
||||
"git.sr.ht/~emersion/soju/config"
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
"git.sr.ht/~emersion/soju/identd"
|
||||
@ -75,6 +76,11 @@ func loadConfig() (*config.Server, *soju.Config, error) {
|
||||
motd = strings.TrimSuffix(string(b), "\n")
|
||||
}
|
||||
|
||||
auth, err := auth.New(raw.Auth.Driver, raw.Auth.Source)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create authenticator: %v", err)
|
||||
}
|
||||
|
||||
if raw.TLS != nil {
|
||||
cert, err := tls.LoadX509KeyPair(raw.TLS.CertPath, raw.TLS.KeyPath)
|
||||
if err != nil {
|
||||
@ -94,6 +100,7 @@ func loadConfig() (*config.Server, *soju.Config, error) {
|
||||
DisableInactiveUsersDelay: raw.DisableInactiveUsersDelay,
|
||||
EnableUsersOnAuth: raw.EnableUsersOnAuth,
|
||||
MOTD: motd,
|
||||
Auth: auth,
|
||||
}
|
||||
return raw, cfg, nil
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ type MsgStore struct {
|
||||
Driver, Source string
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
Driver, Source string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Listen []string
|
||||
TLS *TLS
|
||||
@ -67,6 +71,7 @@ type Server struct {
|
||||
|
||||
DB DB
|
||||
MsgStore MsgStore
|
||||
Auth Auth
|
||||
|
||||
HTTPOrigins []string
|
||||
AcceptProxyIPs IPSet
|
||||
@ -91,6 +96,9 @@ func Defaults() *Server {
|
||||
MsgStore: MsgStore{
|
||||
Driver: "memory",
|
||||
},
|
||||
Auth: Auth{
|
||||
Driver: "internal",
|
||||
},
|
||||
MaxUserNetworks: -1,
|
||||
}
|
||||
}
|
||||
@ -149,6 +157,16 @@ func parse(cfg scfg.Block) (*Server, error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("directive %q: unknown driver %q", d.Name, srv.MsgStore.Driver)
|
||||
}
|
||||
case "auth":
|
||||
if err := d.ParseParams(&srv.Auth.Driver); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch srv.Auth.Driver {
|
||||
case "internal":
|
||||
srv.Auth.Source = ""
|
||||
default:
|
||||
return nil, fmt.Errorf("directive %q: unknown driver %q", d.Name, srv.Auth.Driver)
|
||||
}
|
||||
case "http-origin":
|
||||
srv.HTTPOrigins = d.Params
|
||||
case "accept-proxy-ip":
|
||||
|
@ -185,6 +185,14 @@ The following directives are supported:
|
||||
This can be used together with _disable-inactive-user_ to seamlessly
|
||||
disable and re-enable users during lengthy inactivity.
|
||||
|
||||
*auth* <driver> ...
|
||||
Set the authentication method. By default, internal authentication is used.
|
||||
|
||||
Supported drivers:
|
||||
|
||||
*auth internal*
|
||||
Use internal authentication.
|
||||
|
||||
# IRC SERVICE
|
||||
|
||||
soju exposes an IRC service called *BouncerServ* to manage the bouncer.
|
||||
|
@ -1192,22 +1192,10 @@ func unmarshalUsername(rawUsername string) (username, client, network string) {
|
||||
func (dc *downstreamConn) authenticate(ctx context.Context, username, password string) error {
|
||||
username, clientName, networkName := unmarshalUsername(username)
|
||||
|
||||
u, err := dc.srv.db.GetUser(ctx, username)
|
||||
if err != nil {
|
||||
return newInvalidUsernameOrPasswordError(fmt.Errorf("user not found: %w", err))
|
||||
}
|
||||
|
||||
upgraded, err := u.CheckPassword(password)
|
||||
if err != nil {
|
||||
if err := dc.srv.Config().Auth.AuthPlain(ctx, dc.srv.db, username, password); err != nil {
|
||||
return newInvalidUsernameOrPasswordError(err)
|
||||
}
|
||||
|
||||
if upgraded {
|
||||
if err := dc.srv.db.StoreUser(ctx, u); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dc.user = dc.srv.getUser(username)
|
||||
if dc.user == nil {
|
||||
return fmt.Errorf("user exists in the DB but hasn't been loaded by the bouncer -- a restart may help")
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"gopkg.in/irc.v4"
|
||||
"nhooyr.io/websocket"
|
||||
|
||||
"git.sr.ht/~emersion/soju/auth"
|
||||
"git.sr.ht/~emersion/soju/config"
|
||||
"git.sr.ht/~emersion/soju/database"
|
||||
"git.sr.ht/~emersion/soju/identd"
|
||||
@ -143,6 +144,7 @@ type Config struct {
|
||||
UpstreamUserIPs []*net.IPNet
|
||||
DisableInactiveUsersDelay time.Duration
|
||||
EnableUsersOnAuth bool
|
||||
Auth auth.PlainAuthenticator
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
@ -186,6 +188,7 @@ func NewServer(db database.Database) *Server {
|
||||
srv.config.Store(&Config{
|
||||
Hostname: "localhost",
|
||||
MaxUserNetworks: -1,
|
||||
Auth: auth.NewInternal(),
|
||||
})
|
||||
return srv
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user