2015-03-15 08:06:08 -07:00
|
|
|
// Package auth implements password based user logins.
|
2015-01-04 10:33:53 -08:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2015-02-23 15:51:42 -08:00
|
|
|
"errors"
|
2015-01-15 13:24:12 -08:00
|
|
|
"fmt"
|
2015-01-04 10:33:53 -08:00
|
|
|
"net/http"
|
|
|
|
|
2015-01-12 23:51:25 -08:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2016-12-19 22:45:52 -08:00
|
|
|
"gopkg.in/authboss.v1"
|
|
|
|
"gopkg.in/authboss.v1/internal/response"
|
2015-01-04 10:33:53 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
methodGET = "GET"
|
|
|
|
methodPOST = "POST"
|
|
|
|
|
2015-02-26 12:52:45 -08:00
|
|
|
tplLogin = "login.html.tpl"
|
2015-01-12 21:08:52 -08:00
|
|
|
)
|
2015-01-10 22:49:06 -08:00
|
|
|
|
2015-01-04 10:33:53 -08:00
|
|
|
func init() {
|
2016-05-09 13:20:10 -04:00
|
|
|
authboss.RegisterModule("auth", &Auth{})
|
2015-01-04 10:33:53 -08:00
|
|
|
}
|
|
|
|
|
2015-03-16 14:42:45 -07:00
|
|
|
// Auth module
|
2015-02-23 15:51:42 -08:00
|
|
|
type Auth struct {
|
2015-03-31 15:27:47 -07:00
|
|
|
*authboss.Authboss
|
2015-03-28 09:08:05 -07:00
|
|
|
templates response.Templates
|
2015-01-04 10:33:53 -08:00
|
|
|
}
|
|
|
|
|
2015-03-16 14:42:45 -07:00
|
|
|
// Initialize module
|
2015-03-31 15:27:47 -07:00
|
|
|
func (a *Auth) Initialize(ab *authboss.Authboss) (err error) {
|
|
|
|
a.Authboss = ab
|
|
|
|
|
2016-05-07 02:12:20 -04:00
|
|
|
if a.Storer == nil && a.StoreMaker == nil {
|
2015-03-16 14:42:45 -07:00
|
|
|
return errors.New("auth: Need a Storer")
|
2015-02-23 15:51:42 -08:00
|
|
|
}
|
|
|
|
|
2015-03-31 15:27:47 -07:00
|
|
|
if len(a.XSRFName) == 0 {
|
2015-02-24 15:01:56 -08:00
|
|
|
return errors.New("auth: XSRFName must be set")
|
|
|
|
}
|
|
|
|
|
2015-03-31 15:27:47 -07:00
|
|
|
if a.XSRFMaker == nil {
|
2015-02-24 15:01:56 -08:00
|
|
|
return errors.New("auth: XSRFMaker must be defined")
|
|
|
|
}
|
|
|
|
|
2015-03-31 15:27:47 -07:00
|
|
|
a.templates, err = response.LoadTemplates(a.Authboss, a.Layout, a.ViewsPath, tplLogin)
|
2015-02-20 23:33:35 -08:00
|
|
|
if err != nil {
|
2015-01-18 14:24:20 -08:00
|
|
|
return err
|
2015-01-04 20:41:20 -08:00
|
|
|
}
|
2015-01-04 10:33:53 -08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-03-16 14:42:45 -07:00
|
|
|
// Routes for the module
|
2015-02-23 15:51:42 -08:00
|
|
|
func (a *Auth) Routes() authboss.RouteTable {
|
2015-02-20 23:33:35 -08:00
|
|
|
return authboss.RouteTable{
|
2015-02-25 10:22:55 -08:00
|
|
|
"/login": a.loginHandlerFunc,
|
|
|
|
"/logout": a.logoutHandlerFunc,
|
2015-02-20 23:33:35 -08:00
|
|
|
}
|
2015-01-04 10:33:53 -08:00
|
|
|
}
|
|
|
|
|
2015-03-16 14:42:45 -07:00
|
|
|
// Storage requirements
|
2015-02-23 15:51:42 -08:00
|
|
|
func (a *Auth) Storage() authboss.StorageOptions {
|
2015-02-20 23:33:35 -08:00
|
|
|
return authboss.StorageOptions{
|
2015-03-31 15:27:47 -07:00
|
|
|
a.PrimaryID: authboss.String,
|
2015-02-22 13:16:11 -08:00
|
|
|
authboss.StorePassword: authboss.String,
|
2015-02-20 23:33:35 -08:00
|
|
|
}
|
2015-01-04 10:33:53 -08:00
|
|
|
}
|
|
|
|
|
2015-02-23 15:51:42 -08:00
|
|
|
func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error {
|
2015-01-04 10:33:53 -08:00
|
|
|
switch r.Method {
|
|
|
|
case methodGET:
|
2015-02-23 15:51:42 -08:00
|
|
|
data := authboss.NewHTMLData(
|
2015-03-31 15:27:47 -07:00
|
|
|
"showRemember", a.IsLoaded("remember"),
|
|
|
|
"showRecover", a.IsLoaded("recover"),
|
2015-04-10 12:04:26 -07:00
|
|
|
"showRegister", a.IsLoaded("register"),
|
2015-03-31 15:27:47 -07:00
|
|
|
"primaryID", a.PrimaryID,
|
2015-02-23 15:51:42 -08:00
|
|
|
"primaryIDValue", "",
|
|
|
|
)
|
2015-02-20 23:33:35 -08:00
|
|
|
return a.templates.Render(ctx, w, r, tplLogin, data)
|
2015-01-04 10:33:53 -08:00
|
|
|
case methodPOST:
|
2015-08-02 11:52:23 -07:00
|
|
|
key := r.FormValue(a.PrimaryID)
|
|
|
|
password := r.FormValue("password")
|
2015-01-15 14:01:01 -08:00
|
|
|
|
2015-02-20 23:33:35 -08:00
|
|
|
errData := authboss.NewHTMLData(
|
2015-03-31 15:27:47 -07:00
|
|
|
"error", fmt.Sprintf("invalid %s and/or password", a.PrimaryID),
|
|
|
|
"primaryID", a.PrimaryID,
|
2015-02-23 15:51:42 -08:00
|
|
|
"primaryIDValue", key,
|
2015-03-31 15:27:47 -07:00
|
|
|
"showRemember", a.IsLoaded("remember"),
|
|
|
|
"showRecover", a.IsLoaded("recover"),
|
2015-04-10 12:04:26 -07:00
|
|
|
"showRegister", a.IsLoaded("register"),
|
2015-02-20 23:33:35 -08:00
|
|
|
)
|
|
|
|
|
2015-04-03 11:49:28 -07:00
|
|
|
if valid, err := validateCredentials(ctx, key, password); err != nil {
|
|
|
|
errData["error"] = "Internal server error"
|
2016-05-07 02:12:20 -04:00
|
|
|
fmt.Fprintf(ctx.LogWriter, "auth: validate credentials failed: %v\n", err)
|
2015-04-03 11:49:28 -07:00
|
|
|
return a.templates.Render(ctx, w, r, tplLogin, errData)
|
|
|
|
} else if !valid {
|
2015-08-02 09:52:30 -07:00
|
|
|
if err := a.Callbacks.FireAfter(authboss.EventAuthFail, ctx); err != nil {
|
2016-05-07 02:12:20 -04:00
|
|
|
fmt.Fprintf(ctx.LogWriter, "EventAuthFail callback error'd out: %v\n", err)
|
2015-08-02 09:52:30 -07:00
|
|
|
}
|
2015-02-20 23:33:35 -08:00
|
|
|
return a.templates.Render(ctx, w, r, tplLogin, errData)
|
2015-01-10 23:12:40 -08:00
|
|
|
}
|
2015-01-15 15:10:47 -08:00
|
|
|
|
2015-03-31 15:27:47 -07:00
|
|
|
interrupted, err := a.Callbacks.FireBefore(authboss.EventAuth, ctx)
|
2015-02-25 23:05:14 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if interrupted != authboss.InterruptNone {
|
|
|
|
var reason string
|
|
|
|
switch interrupted {
|
|
|
|
case authboss.InterruptAccountLocked:
|
|
|
|
reason = "Your account has been locked."
|
|
|
|
case authboss.InterruptAccountNotConfirmed:
|
|
|
|
reason = "Your account has not been confirmed."
|
|
|
|
}
|
2015-03-31 15:27:47 -07:00
|
|
|
response.Redirect(ctx, w, r, a.AuthLoginFailPath, "", reason, false)
|
2015-02-25 23:05:14 -08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.SessionStorer.Put(authboss.SessionKey, key)
|
|
|
|
ctx.SessionStorer.Del(authboss.SessionHalfAuthKey)
|
2015-08-02 14:02:14 -07:00
|
|
|
ctx.Values = map[string]string{authboss.CookieRemember: r.FormValue(authboss.CookieRemember)}
|
2015-02-25 23:05:14 -08:00
|
|
|
|
2015-03-31 15:27:47 -07:00
|
|
|
if err := a.Callbacks.FireAfter(authboss.EventAuth, ctx); err != nil {
|
2015-02-25 23:20:02 -08:00
|
|
|
return err
|
|
|
|
}
|
2015-03-31 15:27:47 -07:00
|
|
|
response.Redirect(ctx, w, r, a.AuthLoginOKPath, "", "", true)
|
2015-01-04 10:33:53 -08:00
|
|
|
default:
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
}
|
2015-02-20 23:33:35 -08:00
|
|
|
|
|
|
|
return nil
|
2015-01-04 10:33:53 -08:00
|
|
|
}
|
|
|
|
|
2015-04-03 11:49:28 -07:00
|
|
|
func validateCredentials(ctx *authboss.Context, key, password string) (bool, error) {
|
2015-07-01 18:07:26 -07:00
|
|
|
if err := ctx.LoadUser(key); err == authboss.ErrUserNotFound {
|
|
|
|
return false, nil
|
|
|
|
} else if err != nil {
|
2015-04-03 11:49:28 -07:00
|
|
|
return false, err
|
2015-01-15 13:24:12 -08:00
|
|
|
}
|
|
|
|
|
2015-02-22 13:16:11 -08:00
|
|
|
actualPassword, err := ctx.User.StringErr(authboss.StorePassword)
|
2015-02-20 23:33:35 -08:00
|
|
|
if err != nil {
|
2015-04-03 11:49:28 -07:00
|
|
|
return false, err
|
2015-01-15 13:24:12 -08:00
|
|
|
}
|
|
|
|
|
2015-02-20 23:33:35 -08:00
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(actualPassword), []byte(password)); err != nil {
|
2015-04-03 11:49:28 -07:00
|
|
|
return false, nil
|
2015-01-10 22:49:06 -08:00
|
|
|
}
|
|
|
|
|
2015-04-03 11:49:28 -07:00
|
|
|
return true, nil
|
2015-01-10 22:49:06 -08:00
|
|
|
}
|
|
|
|
|
2015-02-23 15:51:42 -08:00
|
|
|
func (a *Auth) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error {
|
2015-01-04 10:33:53 -08:00
|
|
|
switch r.Method {
|
|
|
|
case methodGET:
|
2015-01-15 16:04:33 -08:00
|
|
|
ctx.SessionStorer.Del(authboss.SessionKey)
|
2015-03-02 22:09:32 -08:00
|
|
|
ctx.CookieStorer.Del(authboss.CookieRemember)
|
|
|
|
ctx.SessionStorer.Del(authboss.SessionLastAction)
|
|
|
|
|
2015-03-31 15:27:47 -07:00
|
|
|
response.Redirect(ctx, w, r, a.AuthLogoutOKPath, "You have logged out", "", true)
|
2015-01-04 10:33:53 -08:00
|
|
|
default:
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
}
|
2015-02-20 23:33:35 -08:00
|
|
|
|
|
|
|
return nil
|
2015-01-04 10:33:53 -08:00
|
|
|
}
|