chldapasswd/lost.go

166 lines
4.3 KiB
Go

package main
import (
"crypto/sha512"
"encoding/base64"
"encoding/binary"
"io"
"log"
"net/http"
"os"
"os/exec"
"time"
"gopkg.in/gomail.v2"
)
func (l LDAPConn) genToken(dn string, previous bool) string {
hour := time.Now()
// Generate the previous token?
if previous {
hour.Add(time.Hour * -1)
}
b := make([]byte, binary.MaxVarintLen64)
binary.PutVarint(b, hour.Round(time.Hour).Unix())
// Search the email address and current password
entries, err := l.GetEntry(dn)
if err != nil {
log.Println("Unable to generate token:", err)
return "#err"
}
email := ""
curpasswd := ""
for _, e := range entries {
if e.Name == "mail" {
email += e.Values[0]
} else if e.Name == "userPassword" {
curpasswd += e.Values[0]
}
}
// Hash that
hash := sha512.New()
hash.Write(b)
hash.Write([]byte(dn))
hash.Write([]byte(email))
hash.Write([]byte(curpasswd))
return base64.StdEncoding.EncodeToString(hash.Sum(nil)[:])
}
func lostPassword(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
displayTmpl(w, "lost.html", map[string]interface{}{})
return
}
// Connect to the LDAP server
conn, err := myLDAP.Connect()
if err != nil || conn == nil {
log.Println(err)
displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]interface{}{"error": err.Error()})
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)
// Search the email address
entries, err := conn.GetEntry(dn)
if err != nil {
log.Println(err)
displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]interface{}{"error": err.Error()})
return
}
email := ""
cn := ""
for _, e := range entries {
if e.Name == "mail" {
email = e.Values[0]
}
if e.Name == "cn" {
cn = e.Values[0]
}
}
if email == "" {
log.Println("Unable to find a valid adress for user " + dn)
displayTmplError(w, http.StatusBadRequest, "lost.html", map[string]interface{}{"error": "We were unable to find a valid email address associated with your account. Please contact an administrator."})
return
}
// Send the email
m := gomail.NewMessage()
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")
var s gomail.Sender
if myLDAP.MailHost != "" {
d := gomail.NewDialer(myLDAP.MailHost, myLDAP.MailPort, myLDAP.MailUser, myLDAP.MailPassword)
s, err = d.Dial()
if err != nil {
log.Println("Unable to connect to email server: " + err.Error())
displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]interface{}{"error": "Unable to connect to email server: " + err.Error()})
return
}
} else {
// Using local sendmail: delegate to the local admin sys the responsability to transport the mail
s = gomail.SendFunc(func(from string, to []string, msg io.WriterTo) error {
cmd := exec.Command("sendmail", "-t")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
pw, err := cmd.StdinPipe()
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
var errs [3]error
_, errs[0] = m.WriteTo(pw)
errs[1] = pw.Close()
errs[2] = cmd.Wait()
for _, err = range errs {
if err != nil {
return err
}
}
return nil
})
}
if err := gomail.Send(s, m); err != nil {
log.Println("Unable to send email: " + err.Error())
displayTmplError(w, http.StatusInternalServerError, "lost.html", map[string]interface{}{"error": "Unable to send email: " + err.Error()})
return
}
displayMsg(w, "Password recovery email sent, check your inbox.", http.StatusOK)
}