maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1package netauth
  2
  3import (
  4	"context"
  5	"fmt"
  6
  7	"github.com/foxcpp/maddy/framework/config"
  8	"github.com/foxcpp/maddy/framework/log"
  9	"github.com/foxcpp/maddy/framework/module"
 10	"github.com/hashicorp/go-hclog"
 11	"github.com/netauth/netauth/pkg/netauth"
 12)
 13
 14const modName = "auth.netauth"
 15
 16func init() {
 17	var _ module.PlainAuth = &Auth{}
 18	var _ module.Table = &Auth{}
 19	module.Register(modName, New)
 20	module.Register("table.netauth", New)
 21}
 22
 23// Auth binds all methods related to the NetAuth client library.
 24type Auth struct {
 25	instName  string
 26	mustGroup string
 27
 28	nacl *netauth.Client
 29
 30	log log.Logger
 31}
 32
 33// New creates a new instance of the NetAuth module.
 34func New(modName, instName string, _, inlineArgs []string) (module.Module, error) {
 35	return &Auth{
 36		instName: instName,
 37		log:      log.Logger{Name: modName},
 38	}, nil
 39}
 40
 41// Init performs deferred initialization actions.
 42func (a *Auth) Init(cfg *config.Map) error {
 43	l := hclog.New(&hclog.LoggerOptions{Output: a.log})
 44	n, err := netauth.NewWithLog(l)
 45	if err != nil {
 46		return err
 47	}
 48	a.nacl = n
 49	a.nacl.SetServiceName("maddy")
 50	cfg.String("require_group", false, false, "", &a.mustGroup)
 51	cfg.Bool("debug", true, false, &a.log.Debug)
 52	if _, err := cfg.Process(); err != nil {
 53		return err
 54	}
 55
 56	a.log.Debugln("Debug logging enabled")
 57	a.log.Debugf("mustGroups status: %s", a.mustGroup)
 58	return nil
 59}
 60
 61// Name returns "auth.netauth" as the fixed module name.
 62func (a *Auth) Name() string {
 63	return modName
 64}
 65
 66// InstanceName returns the configured name for this instance of the
 67// plugin.  Given the way that NetAuth works it doesn't really make
 68// sense to have more than one instance, but this is part of the API.
 69func (a *Auth) InstanceName() string {
 70	return a.instName
 71}
 72
 73// Lookup requests the entity from the remote NetAuth server,
 74// potentially returning that the user does not exist at all.
 75func (a *Auth) Lookup(ctx context.Context, username string) (string, bool, error) {
 76	e, err := a.nacl.EntityInfo(ctx, username)
 77	if err != nil {
 78		return "", false, fmt.Errorf("%s: search: %w", modName, err)
 79	}
 80
 81	if a.mustGroup != "" {
 82		if err := a.checkMustGroup(username); err != nil {
 83			return "", false, err
 84		}
 85	}
 86	return e.GetID(), true, nil
 87}
 88
 89// AuthPlain attempts straightforward authentication of the entity on
 90// the remote NetAuth server.
 91func (a *Auth) AuthPlain(username, password string) error {
 92	a.log.Debugf("attempting to auth user: %s", username)
 93	if err := a.nacl.AuthEntity(context.Background(), username, password); err != nil {
 94		return module.ErrUnknownCredentials
 95	}
 96	a.log.Debugln("netauth returns successful auth")
 97	if a.mustGroup != "" {
 98		if err := a.checkMustGroup(username); err != nil {
 99			return err
100		}
101	}
102	return nil
103}
104
105func (a *Auth) checkMustGroup(username string) error {
106	a.log.Debugf("Performing require_group check: must=%s", a.mustGroup)
107	groups, err := a.nacl.EntityGroups(context.Background(), username)
108	if err != nil {
109		return fmt.Errorf("%s: groups: %w", modName, err)
110	}
111	for _, g := range groups {
112		if g.GetName() == a.mustGroup {
113			return nil
114		}
115	}
116	return fmt.Errorf("%s: missing required group (%s not in %s)", modName, username, a.mustGroup)
117}