diff --git a/lost.go b/lost.go index f4ccbe6..0ddd36f 100644 --- a/lost.go +++ b/lost.go @@ -51,6 +51,25 @@ func (l LDAPConn) genToken(dn string, previous bool) string { return base64.StdEncoding.EncodeToString(hash.Sum(nil)[:]) } +func lostPasswordToken(conn *LDAPConn, login string) (string, string, error) { + // Bind as service to perform the search + err := conn.ServiceBind() + if err != nil { + return "", "", err + } + + // Search the dn of the given user + dn, err := conn.SearchDN(login, true) + if err != nil { + return "", "", err + } + + // Generate the token + token := conn.genToken(dn, false) + + return token, dn, nil +} + func lostPassword(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { displayTmpl(w, "lost.html", map[string]interface{}{}) @@ -65,24 +84,13 @@ func lostPassword(w http.ResponseWriter, r *http.Request) { return } - // Bind as service to perform the search - err = conn.ServiceBind() - if err != nil { - log.Println(err) - displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]interface{}{"error": err.Error()}) - return - } - - // Search the dn of the given user - dn, err := conn.SearchDN(r.PostFormValue("login"), true) - if err != nil { - log.Println(err) - displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]interface{}{"error": err.Error()}) - return - } - // Generate the token - token := conn.genToken(dn, false) + token, dn, err := lostPasswordToken(conn, r.PostFormValue("login")) + if err != nil { + log.Println(err) + displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]interface{}{"error": err.Error()}) + return + } // Search the email address entries, err := conn.GetEntry(dn) @@ -114,7 +122,7 @@ func lostPassword(w http.ResponseWriter, r *http.Request) { m.SetHeader("From", "noreply@nemunai.re") m.SetHeader("To", email) m.SetHeader("Subject", "SSO nemunai.re: password recovery") - m.SetBody("text/plain", "Hello "+cn+"!\n\nSomeone, and we hope it's you, requested to reset your account password. \nIn order to continue, go to:\nhttps://ldap.nemunai.re/reset?l="+r.PostFormValue("login")+"&t="+token+"\n\nBest regards,\n-- \nnemunai.re SSO") + m.SetBody("text/plain", "Hello "+cn+"!\n\nSomeone, and we hope it's you, requested to reset your account password. \nIn order to continue, go to:\n"+BASEURL+"/reset?l="+r.PostFormValue("login")+"&t="+token+"\n\nBest regards,\n-- \nnemunai.re SSO") var s gomail.Sender if myLDAP.MailHost != "" { diff --git a/main.go b/main.go index f1b4436..9ab3e58 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,8 @@ import ( "syscall" ) +const BASEURL = "https://ldap.nemunai.re" + var myLDAP = LDAP{ Host: "localhost", Port: 389, @@ -143,6 +145,35 @@ func main() { myLDAP.MailPassword = val } + if flag.NArg() > 0 { + switch flag.Arg(0) { + case "generate-lost-password-link": + if flag.NArg() != 2 { + log.Fatal("Need a second argument: email of the user to reset") + } + + login := flag.Arg(1) + + conn, err := myLDAP.Connect() + if err != nil || conn == nil { + log.Fatalf("Unable to connect to LDAP: %s", err.Error()) + } + + token, dn, err := lostPasswordToken(conn, login) + if err != nil { + log.Fatal(err.Error()) + } + + fmt.Printf("Reset link for %s: %s/reset?l=%s&t=%s", dn, BASEURL, login, token) + return + case "serve": + case "server": + break + default: + log.Fatalf("%q is not a valid command", flag.Arg(0)) + } + } + // Prepare graceful shutdown interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)