155 lines
3.8 KiB
Go
155 lines
3.8 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"))
|
|
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")
|
|
|
|
// 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)
|
|
}
|