2014-02-07 13:10:01 +03:00
|
|
|
package handler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/dchest/authcookie"
|
|
|
|
"github.com/dchest/passwordreset"
|
|
|
|
"github.com/drone/drone/pkg/database"
|
|
|
|
"github.com/drone/drone/pkg/mail"
|
|
|
|
. "github.com/drone/drone/pkg/model"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// Secret key used to sign auth cookies,
|
|
|
|
// password reset tokens, etc.
|
|
|
|
secret = generateRandomKey(256)
|
|
|
|
)
|
|
|
|
|
|
|
|
// GenerateRandomKey creates a random key of size length bytes
|
|
|
|
func generateRandomKey(strength int) []byte {
|
|
|
|
k := make([]byte, strength)
|
|
|
|
if _, err := io.ReadFull(rand.Reader, k); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return k
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns an HTML index.html page if the user is
|
|
|
|
// not currently authenticated, otherwise redirects
|
|
|
|
// the user to their personal dashboard screen
|
|
|
|
func Index(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
// is the user already authenticated then
|
|
|
|
// redirect to the dashboard page
|
|
|
|
if _, err := r.Cookie("_sess"); err == nil {
|
|
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise redirect to the login page
|
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an HTML form for the User to login.
|
|
|
|
func Login(w http.ResponseWriter, r *http.Request) error {
|
2014-02-15 10:49:38 +03:00
|
|
|
var settings, _ = database.GetSettings()
|
2014-02-13 12:08:20 +03:00
|
|
|
|
|
|
|
data := struct {
|
|
|
|
Settings *Settings
|
|
|
|
}{settings}
|
|
|
|
|
|
|
|
return RenderTemplate(w, "login.html", &data)
|
2014-02-07 13:10:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Terminate the User session.
|
|
|
|
func Logout(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
DelCookie(w, r, "_sess")
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an HTML form for the User to request a password reset.
|
|
|
|
func Forgot(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
return RenderTemplate(w, "forgot.html", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an HTML form for the User to perform a password reset.
|
|
|
|
// This page must be visited from a Password Reset email that
|
|
|
|
// contains a hash to verify the User's identity.
|
|
|
|
func Reset(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
return RenderTemplate(w, "reset.html", &struct{ Error string }{""})
|
|
|
|
}
|
|
|
|
|
2014-02-13 12:08:20 +03:00
|
|
|
// Return an HTML form for the User to signup.
|
|
|
|
func SignUp(w http.ResponseWriter, r *http.Request) error {
|
2014-02-15 10:49:38 +03:00
|
|
|
var settings, _ = database.GetSettings()
|
|
|
|
|
|
|
|
if settings == nil || !settings.OpenInvitations {
|
2014-02-13 12:08:20 +03:00
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
return nil
|
|
|
|
}
|
2014-02-15 10:49:38 +03:00
|
|
|
|
2014-02-13 12:08:20 +03:00
|
|
|
return RenderTemplate(w, "signup.html", nil)
|
|
|
|
}
|
|
|
|
|
2014-02-07 13:10:01 +03:00
|
|
|
// Return an HTML form to register for a new account. This
|
|
|
|
// page must be visited from a Signup email that contains
|
|
|
|
// a hash to verify the Email address is correct.
|
|
|
|
func Register(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
return RenderTemplate(w, "register.html", &struct{ Error string }{""})
|
|
|
|
}
|
|
|
|
|
|
|
|
func ForgotPost(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
email := r.FormValue("email")
|
|
|
|
|
|
|
|
// attempt to retrieve the user by email address
|
|
|
|
user, err := database.GetUserEmail(email)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("could not find user %s to reset password. %s", email, err)
|
|
|
|
// if we can't find the email, we still display
|
|
|
|
// the template to the user. This prevents someone
|
|
|
|
// from trying to guess passwords through trial & error
|
|
|
|
return RenderTemplate(w, "forgot_sent.html", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// hostname from settings
|
|
|
|
hostname := database.SettingsMust().URL().String()
|
|
|
|
|
|
|
|
// generate the password reset token
|
|
|
|
token := passwordreset.NewToken(user.Email, 12*time.Hour, []byte(user.Password), secret)
|
|
|
|
data := struct {
|
|
|
|
Host string
|
|
|
|
User *User
|
|
|
|
Token string
|
|
|
|
}{hostname, user, token}
|
|
|
|
|
|
|
|
// send the email message async
|
|
|
|
go func() {
|
|
|
|
if err := mail.SendPassword(email, data); err != nil {
|
|
|
|
log.Printf("error sending password reset email to %s. %s", email, err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// render the template indicating a success
|
|
|
|
return RenderTemplate(w, "forgot_sent.html", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ResetPost(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
// verify the token and extract the username
|
|
|
|
token := r.FormValue("token")
|
|
|
|
email, err := passwordreset.VerifyToken(token, database.GetPassEmail, secret)
|
|
|
|
if err != nil {
|
|
|
|
return RenderTemplate(w, "reset.html", &struct{ Error string }{"Your password reset request is expired."})
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the user from the database
|
|
|
|
user, err := database.GetUserEmail(email)
|
|
|
|
if err != nil {
|
|
|
|
return RenderTemplate(w, "reset.html", &struct{ Error string }{"Unable to locate user account."})
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the new password
|
|
|
|
password := r.FormValue("password")
|
|
|
|
if err := user.SetPassword(password); err != nil {
|
|
|
|
return RenderTemplate(w, "reset.html", &struct{ Error string }{err.Error()})
|
|
|
|
}
|
|
|
|
|
|
|
|
// save to the database
|
|
|
|
if err := database.SaveUser(user); err != nil {
|
|
|
|
return RenderTemplate(w, "reset.html", &struct{ Error string }{"Unable to update password. Please try again"})
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the user to the session object
|
|
|
|
SetCookie(w, r, "_sess", user.Email)
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-02-13 12:08:20 +03:00
|
|
|
func SignUpPost(w http.ResponseWriter, r *http.Request) error {
|
2014-02-16 04:56:48 +03:00
|
|
|
// if self-registration is disabled we should display an
|
|
|
|
// error message to the user.
|
2014-02-13 12:08:20 +03:00
|
|
|
if !database.SettingsMust().OpenInvitations {
|
2014-02-16 04:56:48 +03:00
|
|
|
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
2014-02-13 12:08:20 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-02-16 04:56:48 +03:00
|
|
|
// generate the password reset token
|
|
|
|
email := r.FormValue("email")
|
|
|
|
token := authcookie.New(email, time.Now().Add(12*time.Hour), secret)
|
|
|
|
|
|
|
|
// get the hostname from the database for use in the email
|
|
|
|
hostname := database.SettingsMust().URL().String()
|
|
|
|
|
|
|
|
// data used to generate the email template
|
|
|
|
data := struct {
|
|
|
|
Host string
|
|
|
|
Email string
|
|
|
|
Token string
|
|
|
|
}{hostname, email, token}
|
|
|
|
|
|
|
|
// send the email message async
|
|
|
|
go mail.SendActivation(email, data)
|
|
|
|
|
|
|
|
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
2014-02-13 12:08:20 +03:00
|
|
|
}
|
|
|
|
|
2014-02-07 13:10:01 +03:00
|
|
|
func RegisterPost(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
// verify the token and extract the username
|
|
|
|
token := r.FormValue("token")
|
|
|
|
email := authcookie.Login(token, secret)
|
|
|
|
if len(email) == 0 {
|
|
|
|
return RenderTemplate(w, "register.html", &struct{ Error string }{"Your registration email is expired."})
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the email and name
|
2014-02-09 00:07:02 +03:00
|
|
|
user := NewUser(r.FormValue("name"), email)
|
2014-02-07 13:10:01 +03:00
|
|
|
|
|
|
|
// set the new password
|
|
|
|
password := r.FormValue("password")
|
|
|
|
if err := user.SetPassword(password); err != nil {
|
|
|
|
return RenderTemplate(w, "register.html", &struct{ Error string }{err.Error()})
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify fields are correct
|
|
|
|
if err := user.Validate(); err != nil {
|
|
|
|
return RenderTemplate(w, "register.html", &struct{ Error string }{err.Error()})
|
|
|
|
}
|
|
|
|
|
|
|
|
// save to the database
|
2014-02-09 00:07:02 +03:00
|
|
|
if err := database.SaveUser(user); err != nil {
|
2014-02-07 13:10:01 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the user to the session object
|
|
|
|
SetCookie(w, r, "_sess", user.Email)
|
|
|
|
|
|
|
|
// redirect the user to their dashboard
|
|
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
|
|
return nil
|
|
|
|
}
|