maddy

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

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

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package plain_separate
 20
 21import (
 22	"context"
 23	"errors"
 24	"fmt"
 25
 26	"github.com/foxcpp/maddy/framework/config"
 27	modconfig "github.com/foxcpp/maddy/framework/config/module"
 28	"github.com/foxcpp/maddy/framework/log"
 29	"github.com/foxcpp/maddy/framework/module"
 30)
 31
 32type Auth struct {
 33	modName  string
 34	instName string
 35
 36	userTbls []module.Table
 37	passwd   []module.PlainAuth
 38
 39	onlyFirstID bool
 40
 41	Log log.Logger
 42}
 43
 44func NewAuth(modName, instName string, _, inlinargs []string) (module.Module, error) {
 45	a := &Auth{
 46		modName:     modName,
 47		instName:    instName,
 48		onlyFirstID: false,
 49		Log:         log.Logger{Name: modName},
 50	}
 51
 52	if len(inlinargs) != 0 {
 53		return nil, errors.New("plain_separate: inline arguments are not used")
 54	}
 55
 56	return a, nil
 57}
 58
 59func (a *Auth) Name() string {
 60	return a.modName
 61}
 62
 63func (a *Auth) InstanceName() string {
 64	return a.instName
 65}
 66
 67func (a *Auth) Init(cfg *config.Map) error {
 68	cfg.Bool("debug", false, false, &a.Log.Debug)
 69	cfg.Callback("user", func(m *config.Map, node config.Node) error {
 70		var tbl module.Table
 71		err := modconfig.ModuleFromNode("table", node.Args, node, m.Globals, &tbl)
 72		if err != nil {
 73			return err
 74		}
 75
 76		a.userTbls = append(a.userTbls, tbl)
 77		return nil
 78	})
 79	cfg.Callback("pass", func(m *config.Map, node config.Node) error {
 80		var auth module.PlainAuth
 81		err := modconfig.ModuleFromNode("auth", node.Args, node, m.Globals, &auth)
 82		if err != nil {
 83			return err
 84		}
 85
 86		a.passwd = append(a.passwd, auth)
 87		return nil
 88	})
 89
 90	if _, err := cfg.Process(); err != nil {
 91		return err
 92	}
 93
 94	return nil
 95}
 96
 97func (a *Auth) Lookup(ctx context.Context, username string) (string, bool, error) {
 98	ok := len(a.userTbls) == 0
 99	for _, tbl := range a.userTbls {
100		_, tblOk, err := tbl.Lookup(ctx, username)
101		if err != nil {
102			return "", false, fmt.Errorf("plain_separate: underlying table error: %w", err)
103		}
104		if tblOk {
105			ok = true
106			break
107		}
108	}
109	if !ok {
110		return "", false, nil
111	}
112	return "", true, nil
113}
114
115func (a *Auth) AuthPlain(username, password string) error {
116	ok := len(a.userTbls) == 0
117	for _, tbl := range a.userTbls {
118		_, tblOk, err := tbl.Lookup(context.TODO(), username)
119		if err != nil {
120			return err
121		}
122		if tblOk {
123			ok = true
124			break
125		}
126	}
127	if !ok {
128		return errors.New("user not found in tables")
129	}
130
131	var lastErr error
132	for _, p := range a.passwd {
133		if err := p.AuthPlain(username, password); err != nil {
134			lastErr = err
135			continue
136		}
137
138		return nil
139	}
140	return lastErr
141}
142
143func init() {
144	module.Register("auth.plain_separate", NewAuth)
145}