1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package plain_separate2021import (22 "context"23 "errors"24 "fmt"2526 "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)3132type Auth struct {33 modName string34 instName string3536 userTbls []module.Table37 passwd []module.PlainAuth3839 onlyFirstID bool4041 Log log.Logger42}4344func 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 }5152 if len(inlinargs) != 0 {53 return nil, errors.New("plain_separate: inline arguments are not used")54 }5556 return a, nil57}5859func (a *Auth) Name() string {60 return a.modName61}6263func (a *Auth) InstanceName() string {64 return a.instName65}6667func (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.Table71 err := modconfig.ModuleFromNode("table", node.Args, node, m.Globals, &tbl)72 if err != nil {73 return err74 }7576 a.userTbls = append(a.userTbls, tbl)77 return nil78 })79 cfg.Callback("pass", func(m *config.Map, node config.Node) error {80 var auth module.PlainAuth81 err := modconfig.ModuleFromNode("auth", node.Args, node, m.Globals, &auth)82 if err != nil {83 return err84 }8586 a.passwd = append(a.passwd, auth)87 return nil88 })8990 if _, err := cfg.Process(); err != nil {91 return err92 }9394 return nil95}9697func (a *Auth) Lookup(ctx context.Context, username string) (string, bool, error) {98 ok := len(a.userTbls) == 099 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 = true106 break107 }108 }109 if !ok {110 return "", false, nil111 }112 return "", true, nil113}114115func (a *Auth) AuthPlain(username, password string) error {116 ok := len(a.userTbls) == 0117 for _, tbl := range a.userTbls {118 _, tblOk, err := tbl.Lookup(context.TODO(), username)119 if err != nil {120 return err121 }122 if tblOk {123 ok = true124 break125 }126 }127 if !ok {128 return errors.New("user not found in tables")129 }130131 var lastErr error132 for _, p := range a.passwd {133 if err := p.AuthPlain(username, password); err != nil {134 lastErr = err135 continue136 }137138 return nil139 }140 return lastErr141}142143func init() {144 module.Register("auth.plain_separate", NewAuth)145}