package main import ( "bytes" "context" "encoding/json" "errors" "fmt" "net/http" "os" "os/signal" "strings" "syscall" "github.com/jackc/pgx/v5" "github.com/lor00x/goldap/message" ldap "github.com/vjeantet/ldapserver" ) func auth(username, email, password string) (ruser, remail string, err error) { var payload struct { Secret string Username string Email string Password string } payload.Secret = os.Getenv("AUTH_SECRET") payload.Username = username payload.Email = email payload.Password = password raw, err := json.Marshal(payload) if err != nil { return } body := bytes.NewReader(raw) req, err := http.NewRequest("POST", os.Getenv("AUTH_URL"), body) if err != nil { return } req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return } defer resp.Body.Close() var result struct { Username string Email string Error string } err = json.NewDecoder(resp.Body).Decode(&result) if err != nil { return } if result.Error != "" { err = errors.New(result.Error) } ruser = result.Username remail = result.Email return } func bind(w ldap.ResponseWriter, m *ldap.Message) { r := m.GetBindRequest() res := ldap.NewBindResponse(ldap.LDAPResultSuccess) username := string(r.Name()) password := string(r.AuthenticationSimple()) if username == os.Getenv("LDAP_USER") { if password == os.Getenv("LDAP_PASS") { w.Write(res) return } } if username == "root" { res.SetResultCode(ldap.LDAPResultInvalidCredentials) res.SetDiagnosticMessage("root login is disabled") w.Write(res) return } _, _, err := auth(username, "", password) if err == nil { fmt.Println("bind:", username, "ok") w.Write(res) return } fmt.Println("bind:", username, "incorrect password") res.SetResultCode(ldap.LDAPResultInvalidCredentials) res.SetDiagnosticMessage("invalid credentials") w.Write(res) } func updateEmail(username, email string) (err error) { path := "dbname=" + os.Getenv("DATABASE") conn, err := pgx.Connect(context.Background(), path) if err != nil { return } defer conn.Close(context.Background()) query := "update \"user\" set email=$1 where username=$2" _, err = conn.Exec(context.Background(), query, email, username) return } func search(w ldap.ResponseWriter, m *ldap.Message) { r := m.GetSearchRequest() res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess) defer w.Write(res) var username, email string s := r.FilterString() s = s[6 : len(s)-1] if strings.Contains(s, "@") { username, email, _ = auth("", s, "") } else { username, email, _ = auth(s, "", "") } if username == "" || email == "" { fmt.Println("search:", s, "not found") return } fmt.Println("search:", s, "found", username, email) err := updateEmail(username, email) if err != nil { fmt.Println(err) } e := ldap.NewSearchResultEntry(strings.ToLower(username)) e.AddAttribute("uid", message.AttributeValue(strings.ToLower(username))) e.AddAttribute("mail", message.AttributeValue(strings.ToLower(email))) w.Write(e) } func main() { ldap.Logger = ldap.DiscardingLogger server := ldap.NewServer() routes := ldap.NewRouteMux() routes.Bind(bind) routes.Search(search) server.Handle(routes) go server.ListenAndServe(":10389") ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) <-ch close(ch) server.Stop() }