diff --git a/dovecot/keyproxy.go b/dovecot/keyproxy.go index 61887b76eb45f0dbf331934eeaa8741efbff5274..c8b84f2d88cc902e520d5df0be02fcc261320f5d 100644 --- a/dovecot/keyproxy.go +++ b/dovecot/keyproxy.go @@ -10,6 +10,7 @@ import ( "git.autistici.org/ai3/go-common/clientutil" "git.autistici.org/ai3/go-common/userenckey" + "golang.org/x/sync/singleflight" "git.autistici.org/id/keystore/backend" ldapBE "git.autistici.org/id/keystore/backend/ldap" @@ -82,6 +83,7 @@ var passwordSep = "/" type KeyLookupProxy struct { config *Config keystore client.Client + sfgroup singleflight.Group db backend.Database } @@ -151,7 +153,29 @@ func (s *KeyLookupProxy) lookupUserdb(ctx context.Context, username string) (int return newUserDBResponse(s.b64encode(pub)), true, nil } +type passdbLookupResult struct { + obj interface{} + ok bool +} + func (s *KeyLookupProxy) lookupPassdb(ctx context.Context, username, password string) (interface{}, bool, error) { + // In order to wrap lookupPassdb() in a singleflight group we wrap its + // return parameters in a struct, with some additional error handling. + res, err, _ := s.sfgroup.Do(username, func() (interface{}, error) { + obj, ok, err := s.lookupPassdbReal(ctx, username, password) + if err != nil { + return nil, err + } + return &passdbLookupResult{obj: obj, ok: ok}, nil + }) + if err != nil { + return nil, false, err + } + plr := res.(*passdbLookupResult) + return plr.obj, plr.ok, nil +} + +func (s *KeyLookupProxy) lookupPassdbReal(ctx context.Context, username, password string) (interface{}, bool, error) { // The password might be a SSO token, so first of all we try // to fetch the unencrypted key from the keystore daemon. var keystoreStatus string