view controllers/pwreset.go @ 303:75e32633fb96

Makefile: Force rebuild of dependencies when 3rdpartylibs.sh changes.
author Sascha Wilde <wilde@intevation.de>
date Wed, 01 Aug 2018 13:37:48 +0200
parents 0777aa6de45b
children 69e291f26bbd
line wrap: on
line source

package controllers

import (
	"database/sql"
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"strings"

	"gemma.intevation.de/gemma/auth"
	"gemma.intevation.de/gemma/config"
	gomail "gopkg.in/gomail.v2"
)

const (
	userExistsSQL = `SELECT email_address
    FROM users.list_users WHERE username = $1
    LIMIT 1`
)

const (
	mailTmpl = `You have requested a password change for your account on
%s://%s

Please follow this link to get to the page where you can change your password.

%s://%s/#/users/passwordreset/%s

The link is only valid for one hour.

Best regards
    Your service team`
)

func messageBody(https bool, hash, serverName string) string {

	var proto string

	if https {
		proto = "https"
	} else {
		proto = "http"
	}

	return fmt.Sprintf(mailTmpl,
		proto, serverName,
		proto, serverName,
		hash)
}

const hashLength = 32

func generateHash() string {
	return hex.EncodeToString(auth.GenerateRandomKey(hashLength))
}

func passwordReset(
	input interface{},
	req *http.Request,
	db *sql.DB,
) (jr JSONResult, err error) {

	log.Println("passwordreset")

	user := input.(*PWResetUser)

	if user.User == "" {
		err = JSONError{http.StatusBadRequest, "Invalid user name"}
		return
	}

	cfg := &config.Config

	if db, err = auth.OpenDB(cfg.ServiceUser, cfg.ServicePassword); err != nil {
		return
	}
	defer db.Close()

	var email string
	err = db.QueryRow(userExistsSQL, user.User).Scan(&email)

	switch {
	case err == sql.ErrNoRows:
		err = JSONError{http.StatusNotFound, "User does not exist."}
		return
	case err != nil:
		return
	}

	hash := generateHash()

	serverName := req.Host
	useHTTPS := strings.ToLower(req.URL.Scheme) == "https"

	body := messageBody(useHTTPS, hash, serverName)

	m := gomail.NewMessage()
	m.SetHeader("From", cfg.MailFrom)
	m.SetHeader("To", email)
	m.SetHeader("Subject", "Password Reset Link")
	m.SetBody("text/plain", body)

	d := gomail.Dialer{
		Host:      cfg.MailHost,
		Port:      int(cfg.MailPort),
		Username:  cfg.MailUser,
		Password:  cfg.MailPassword,
		LocalName: cfg.MailHelo,
		SSL:       cfg.MailPort == 465,
	}

	if err = d.DialAndSend(m); err != nil {
		return
	}

	// TODO: Keep hash/user for one hour or till resolved.

	jr.Result = &struct {
		SendTo string `json:"send-to"`
	}{
		SendTo: email,
	}
	return
}