1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-01-06 03:54:17 +02:00

Reworking auth

This commit is contained in:
Kris Runzer 2015-02-20 23:33:35 -08:00
parent 0840d9743c
commit 1198466d76
10 changed files with 88 additions and 563 deletions

View File

@ -3,66 +3,42 @@ package auth
import (
"errors"
"fmt"
"html/template"
"net/http"
"golang.org/x/crypto/bcrypt"
"gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/views"
"gopkg.in/authboss.v0/internal/render"
)
const (
methodGET = "GET"
methodPOST = "POST"
pageLogin = "login.tpl"
tplLogin = "login.tpl"
attrUsername = "username"
attrPassword = "password"
storeUsername = "username"
storePassword = "password"
)
func init() {
a := &Auth{}
a := &AuthModule{}
authboss.RegisterModule("auth", a)
}
type AuthPage struct {
Error string
Username string
ShowRemember bool
ShowRecover bool
FlashSuccess string
FlashError string
XSRFName string
XSRFToken string
}
type Auth struct {
routes authboss.RouteTable
storageOptions authboss.StorageOptions
templates map[string]*template.Template
type AuthModule struct {
templates render.Templates
policies []authboss.Validator
isRememberLoaded bool
isRecoverLoaded bool
}
func (a *Auth) Initialize() (err error) {
if a.templates, err = views.Get(authboss.Cfg.Layout, authboss.Cfg.ViewsPath, pageLogin); err != nil {
func (a *AuthModule) Initialize() (err error) {
a.templates, err = render.LoadTemplates(authboss.Cfg.Layout, authboss.Cfg.ViewsPath, tplLogin)
if err != nil {
return err
}
a.routes = authboss.RouteTable{
"login": a.loginHandlerFunc,
"logout": a.logoutHandlerFunc,
}
a.storageOptions = authboss.StorageOptions{
attrUsername: authboss.String,
attrPassword: authboss.String,
}
a.policies = authboss.FilterValidators(authboss.Cfg.Policies, "username", "password")
a.isRememberLoaded = authboss.IsLoaded("remember")
a.isRecoverLoaded = authboss.IsLoaded("recover")
@ -70,15 +46,21 @@ func (a *Auth) Initialize() (err error) {
return nil
}
func (a *Auth) Routes() authboss.RouteTable {
return a.routes
func (a *AuthModule) Routes() authboss.RouteTable {
return authboss.RouteTable{
"login": a.loginHandlerFunc,
"logout": a.logoutHandlerFunc,
}
}
func (a *Auth) Storage() authboss.StorageOptions {
return a.storageOptions
func (a *AuthModule) Storage() authboss.StorageOptions {
return authboss.StorageOptions{
storeUsername: authboss.String,
storePassword: authboss.String,
}
}
func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
func (a *AuthModule) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error {
switch r.Method {
case methodGET:
if _, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok {
@ -87,96 +69,64 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
}
}
page := AuthPage{
ShowRemember: a.isRememberLoaded,
ShowRecover: a.isRecoverLoaded,
XSRFName: authboss.Cfg.XSRFName,
XSRFToken: authboss.Cfg.XSRFMaker(w, r),
}
if msg, ok := ctx.SessionStorer.Get(authboss.FlashSuccessKey); ok {
page.FlashSuccess = msg
ctx.SessionStorer.Del(authboss.FlashSuccessKey)
}
tpl := a.templates[pageLogin]
tpl.Execute(w, page)
data := authboss.NewHTMLData("showRemember", a.isRememberLoaded, "showRecover", a.isRecoverLoaded)
return a.templates.Render(ctx, w, r, tplLogin, data)
case methodPOST:
u, ok := ctx.FirstPostFormValue("username")
if !ok {
fmt.Fprintln(authboss.Cfg.LogWriter, errors.New("auth: Expected postFormValue 'username' to be in the context"))
interrupted, err := authboss.Cfg.Callbacks.FireBefore(authboss.EventAuth, ctx)
if err != nil {
return err
} else if interrupted {
return errors.New("auth interrupted")
}
if err := authboss.Cfg.Callbacks.FireBefore(authboss.EventAuth, ctx); err != nil {
w.WriteHeader(http.StatusForbidden)
username, _ := ctx.FirstPostFormValue("username")
password, _ := ctx.FirstPostFormValue("password")
tpl := a.templates[pageLogin]
tpl.ExecuteTemplate(w, tpl.Name(), AuthPage{
Error: err.Error(),
Username: u,
ShowRemember: a.isRememberLoaded,
ShowRecover: a.isRecoverLoaded,
XSRFName: authboss.Cfg.XSRFName,
XSRFToken: authboss.Cfg.XSRFMaker(w, r),
})
errData := authboss.NewHTMLData(
"error", "invalid username and/or password",
"username", username,
"showRemember", a.isRememberLoaded,
"showRecover", a.isRecoverLoaded,
)
if validationErrs := ctx.Validate(a.policies); len(validationErrs) > 0 {
fmt.Fprintln(authboss.Cfg.LogWriter, "auth: form validation failed:", validationErrs.Map())
return a.templates.Render(ctx, w, r, tplLogin, errData)
}
p, ok := ctx.FirstPostFormValue("password")
if !ok {
fmt.Fprintln(authboss.Cfg.LogWriter, errors.New("auth: Expected postFormValue 'password' to be in the context"))
if err := validateCredentials(ctx, username, password); err != nil {
fmt.Fprintln(authboss.Cfg.LogWriter, "auth: failed to validate credentials:", err)
return a.templates.Render(ctx, w, r, tplLogin, errData)
}
if err := a.authenticate(ctx, u, p); err != nil {
fmt.Fprintln(authboss.Cfg.LogWriter, err)
w.WriteHeader(http.StatusForbidden)
tpl := a.templates[pageLogin]
tpl.ExecuteTemplate(w, tpl.Name(), AuthPage{
Error: "invalid username and/or password",
Username: u,
ShowRemember: a.isRememberLoaded,
ShowRecover: a.isRecoverLoaded,
XSRFName: authboss.Cfg.XSRFName,
XSRFToken: authboss.Cfg.XSRFMaker(w, r),
})
return
}
ctx.SessionStorer.Put(authboss.SessionKey, u)
ctx.SessionStorer.Put(authboss.SessionKey, username)
authboss.Cfg.Callbacks.FireAfter(authboss.EventAuth, ctx)
http.Redirect(w, r, authboss.Cfg.AuthLoginSuccessRoute, http.StatusFound)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func (a *Auth) authenticate(ctx *authboss.Context, username, password string) error {
var userInter interface{}
var err error
if userInter, err = authboss.Cfg.Storer.Get(username, nil); err != nil {
return err
}
ctx.User = authboss.Unbind(userInter)
pwdIntf, ok := ctx.User[attrPassword]
if !ok {
return errors.New("auth: User attributes did not include a password.")
}
pwd, ok := pwdIntf.(string)
if !ok {
return errors.New("auth: User password was not a string somehow.")
}
if err := bcrypt.CompareHashAndPassword([]byte(pwd), []byte(password)); err != nil {
return errors.New("invalid password")
}
return nil
}
func (a *Auth) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
func validateCredentials(ctx *authboss.Context, username, password string) error {
if err := ctx.LoadUser(username); err != nil {
return err
}
actualPassword, err := ctx.User.StringErr(storePassword)
if err != nil {
return err
}
if err := bcrypt.CompareHashAndPassword([]byte(actualPassword), []byte(password)); err != nil {
return err
}
return nil
}
func (a *AuthModule) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error {
switch r.Method {
case methodGET:
ctx.SessionStorer.Del(authboss.SessionKey)
@ -184,4 +134,6 @@ func (a *Auth) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
return nil
}

View File

@ -1,262 +1 @@
package auth
/*import (
"bytes"
"html/template"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"gopkg.in/authboss.v0"
)
func getCompiledTemplate(path string, data interface{}) (b *bytes.Buffer, err error) {
var file []byte
if file, err = ioutil.ReadFile(path); err != nil {
return nil, err
}
var tpl *template.Template
if tpl, err = template.New("tpl").Parse(string(file)); err != nil {
return nil, err
}
b = &bytes.Buffer{}
if err = tpl.Execute(b, data); err != nil {
return nil, err
}
return b, nil
}
func TestAuth_Storage(t *testing.T) {
a := &Auth{}
if err := a.Initialize(); err != nil {
t.Errorf("Unexpected config error: %v", err)
}
options := a.Storage()
tests := []struct {
Name string
Type authboss.DataType
}{
{"username", authboss.String},
{"password", authboss.String},
}
for i, test := range tests {
if value, ok := options[test.Name]; !ok {
t.Errorf("%d> Expected key %s", i, test.Name)
continue
} else if value != test.Type {
t.Errorf("$d> Expected key %s to have value %v, got %v", i, test.Name, test.Type, value)
continue
}
}
}
func TestAuth_Routes(t *testing.T) {
a := &Auth{}
if err := a.Initialize(); err != nil {
t.Errorf("Unexpected config error: %v", err)
}
routes := a.Routes()
tests := []struct {
Route string
}{
{"login"},
{"logout"},
}
for i, test := range tests {
if value, ok := routes[test.Route]; !ok {
t.Errorf("%d> Expected key %s", i, test.Route)
} else if value == nil {
t.Errorf("%d> Expected key %s to have func", i, test.Route)
}
}
}
func TestAuth_loginHandlerFunc_GET(t *testing.T) {
a := &Auth{}
if err := a.Initialize(); err != nil {
t.Errorf("Unexpected config error: %v", err)
}
r, err := http.NewRequest("GET", "/login", nil)
if err != nil {
t.Errorf("Unexpected error '%s'", err)
}
w := httptest.NewRecorder()
ctx, err := authboss.ContextFromRequest(r)
if err != nil {
t.Errorf("Unexpected error '%s'", err)
}
ctx.SessionStorer = testClientStorer{}
a.loginHandlerFunc(ctx, w, r)
if tpl, err := getCompiledTemplate("views/login.tpl", nil); err != nil {
t.Errorf("Unexpected error '%s'", err)
} else {
if !bytes.Equal(tpl.Bytes(), w.Body.Bytes()) {
t.Errorf("Expected '%s', got '%s'", tpl.Bytes(), w.Body.Bytes())
}
}
}
func TestAuth_loginHandlerFunc_POST(t *testing.T) {
tests := []struct {
Username, Password string
StatusCode int
LoginSuccess bool
Location string
BodyData *AuthPage
}{
{"john", "1234", http.StatusFound, true, "/dashboard", nil},
{"jane", "1234", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false, false, "", ""}},
{"mike", "", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false, false, "", ""}},
}
authboss.Cfg.Storer = NewMockUserStorer()
authboss.Cfg.AuthLoginSuccessRoute = "/dashboard"
for i, test := range tests {
a := &Auth{}
if err := a.Initialize(); err != nil {
t.Errorf("%d> Unexpected config error: %v", i, err)
continue
}
postData := url.Values{}
postData.Set("username", test.Username)
postData.Set("password", test.Password)
r, err := http.NewRequest("POST", "/login", strings.NewReader(postData.Encode()))
if err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
continue
}
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
ctx, err := authboss.ContextFromRequest(r)
if err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
continue
}
ctx.SessionStorer = testClientStorer{}
a.loginHandlerFunc(ctx, w, r)
if test.LoginSuccess {
if val, ok := ctx.SessionStorer.Get(authboss.SessionKey); !ok {
t.Errorf("%d> Expected login to be present", i)
} else if val != test.Username {
t.Errorf("%d> Expected username %s, got %s", i, test.Username, val)
}
}
if test.StatusCode != w.Code {
t.Errorf("%d> Expected status code %d, got %d", i, test.StatusCode, w.Code)
continue
}
location := w.Header().Get("Location")
if test.Location != location {
t.Errorf("%d> Expected lcoation %s, got %s", i, test.Location, location)
continue
}
if test.BodyData != nil {
if tpl, err := getCompiledTemplate("views/login.tpl", test.BodyData); err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
continue
} else {
if !bytes.Equal(tpl.Bytes(), w.Body.Bytes()) {
t.Errorf("%d> Expected '%s', got '%s'", i, tpl.Bytes(), w.Body.Bytes())
continue
}
}
}
}
}
func TestAuth_loginHandlerFunc_OtherMethods(t *testing.T) {
a := Auth{}
methods := []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"}
for i, method := range methods {
r, err := http.NewRequest(method, "/login", nil)
if err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
}
w := httptest.NewRecorder()
a.loginHandlerFunc(nil, w, r)
if http.StatusMethodNotAllowed != w.Code {
t.Errorf("%d> Expected status code %d, got %d", i, http.StatusMethodNotAllowed, w.Code)
continue
}
}
}
func TestAuth_logoutHandlerFunc_GET(t *testing.T) {
authboss.Cfg.AuthLogoutRoute = "/dashboard"
a := Auth{}
if err := a.Initialize(); err != nil {
t.Errorf("Unexpeced config error '%s'", err)
}
r, err := http.NewRequest("GET", "/logout", nil)
if err != nil {
t.Errorf("Unexpected error '%s'", err)
}
w := httptest.NewRecorder()
ctx, err := authboss.ContextFromRequest(r)
if err != nil {
t.Errorf("Unexpected error '%s'", err)
}
ctx.SessionStorer = testClientStorer{authboss.SessionKey: "asdf"}
a.logoutHandlerFunc(ctx, w, r)
if _, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok {
t.Errorf("Expected to be logged out")
}
if http.StatusFound != w.Code {
t.Errorf("Expected status code %d, got %d", http.StatusFound, w.Code)
}
location := w.Header().Get("Location")
if location != "/dashboard" {
t.Errorf("Expected lcoation %s, got %s", "/dashboard", location)
}
}
func TestAuth_logoutHandlerFunc_OtherMethods(t *testing.T) {
a := Auth{}
methods := []string{"HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"}
for i, method := range methods {
r, err := http.NewRequest(method, "/logout", nil)
if err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
}
w := httptest.NewRecorder()
a.logoutHandlerFunc(nil, w, r)
if http.StatusMethodNotAllowed != w.Code {
t.Errorf("%d> Expected status code %d, got %d", i, http.StatusMethodNotAllowed, w.Code)
continue
}
}
}
*/

View File

@ -1,59 +0,0 @@
package auth
import (
"errors"
"strings"
"gopkg.in/authboss.v0"
)
type MockUser struct {
Username, Password string
}
type MockUserStorer struct {
Users []MockUser
}
func NewMockUserStorer() *MockUserStorer {
return &MockUserStorer{
Users: []MockUser{
{"John", "$2a$10$0hwgO.5fThx0DOHbErIxaemMTrU3RDNJchM6ToMOmFf.hkuX4RKRK"}, // 1234
{"Jane", "$2a$10$tzIH0BU8BpOOsf768Iv4KecouL0gPgrvCpYZpBwJozlqezfabBpr2"}, // asdf
},
}
}
func (s MockUserStorer) Create(key string, attr authboss.Attributes) error {
return errors.New("Not implemented")
}
func (s MockUserStorer) Put(key string, attr authboss.Attributes) error {
return errors.New("Not implemented")
}
func (s MockUserStorer) Get(key string, attrMeta authboss.AttributeMeta) (result interface{}, err error) {
for _, u := range s.Users {
if strings.EqualFold(u.Username, key) {
return u, nil
}
}
return nil, errors.New("User not found")
}
type testClientStorer map[string]string
func (t testClientStorer) Put(key, value string) {
t[key] = value
}
func (t testClientStorer) Get(key string) (string, bool) {
s, ok := t[key]
return s, ok
}
func (t testClientStorer) Del(key string) {
delete(t, key)
}

View File

@ -73,10 +73,10 @@ func NewConfig() *Config {
HostName: "localhost:8080",
BCryptCost: bcrypt.DefaultCost,
Layout: template.Must(template.New("").Parse(`{{template "authboss" .}}`)),
LayoutEmail: template.Must(template.New("").Parse(`{{template "authboss" .}}`)),
Layout: template.Must(template.New("").Parse(`<html><body>{{template "authboss" .}}</body></html>`)),
LayoutEmail: template.Must(template.New("").Parse(`<html><body>{{template "authboss" .}}</body></html>`)),
AuthLogoutRoute: "/",
AuthLogoutRoute: "/login",
AuthLoginSuccessRoute: "/",
Policies: []Validator{

View File

@ -76,7 +76,7 @@ func confirm_email_html_tpl() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "confirm_email.html.tpl", size: 26, mode: os.FileMode(438), modTime: time.Unix(1424060528, 0)}
info := bindata_file_info{name: "confirm_email.html.tpl", size: 26, mode: os.FileMode(438), modTime: time.Unix(1424471280, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -96,52 +96,12 @@ func confirm_email_txt_tpl() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "confirm_email.txt.tpl", size: 9, mode: os.FileMode(438), modTime: time.Unix(1424060528, 0)}
info := bindata_file_info{name: "confirm_email.txt.tpl", size: 9, mode: os.FileMode(438), modTime: time.Unix(1424471280, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _layout_tpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x95\xc1\x72\xd3\x30\x10\x86\xef\x79\x0a\x8d\x0e\xdc\x2c\xd1\x26\xc0\x4c\xeb\xe4\x06\x2f\xc0\x13\xc8\xd6\x3a\x56\x90\x25\xa3\x5d\xb7\xc9\x78\xfa\xee\xc8\x96\x1b\x5c\xd7\x40\x87\x03\x33\xe4\x60\x67\x37\xda\xff\xdf\xfd\x34\x52\xf2\x9a\x1a\x7b\xd8\xe4\x35\x28\x7d\xd8\xb0\xf8\xc9\xad\x71\xdf\x58\x1d\xa0\xda\x73\x29\x1d\x90\x76\x4a\x14\xde\x13\x52\x50\x6d\xa9\x9d\x28\x7d\x23\x2b\xef\x28\x53\x8f\x80\xbe\x01\xb9\x13\xef\xc5\x56\x96\x88\x2f\xd2\x22\x26\x38\x0b\x60\xf7\x1c\xe9\x62\x01\x6b\x00\xe2\x4c\xae\xd9\x34\xea\x3c\x28\xbf\xb2\xb9\x26\xe4\x56\x6c\xc5\xcd\xe8\x71\xcd\x89\xc6\xb8\x3f\x98\x60\x19\x4c\x4b\x0c\x43\xb9\xe7\x35\x51\x8b\x77\x52\xaa\x93\x3a\x8b\xa3\xf7\x47\x0b\xaa\x35\x38\xfa\x0c\x39\x69\x4d\x81\xf2\xf4\xbd\x83\x70\x91\xb7\xe2\x26\x8e\x94\x82\xd1\xe7\x84\xfc\x90\xcb\xa4\xb7\x22\xfe\xd6\x11\x6e\xe5\x69\x39\x41\x54\x66\x74\x69\x61\xcf\x09\xce\x24\x4f\xea\x41\x25\xe5\xb9\x61\x2e\xd3\x0e\xe5\x85\xd7\x97\xf8\xd2\xe6\x81\x95\x56\x21\xee\x79\x19\x99\x2b\xe3\x20\x64\x95\xed\x8c\xe6\xa9\xbb\xbe\x37\x15\x13\x5f\xe2\x92\xfa\x6b\x57\x96\x80\xf8\xf4\x94\xda\x9e\x95\x06\xff\x38\x2d\x5f\xfe\x52\x7a\x9b\x9d\x31\xf3\x55\x85\x40\xd9\x96\x0d\x71\xa3\xb3\x8f\xb3\xe5\xcb\x12\x65\x21\x10\x1b\x9f\x19\x26\xcb\x29\xd2\x06\x1b\x83\xa8\x0a\x0b\x9c\x8d\xdb\xb4\xe7\x8d\x0a\x47\xe3\x32\xf2\xed\x1d\xfb\xf4\xa1\x3d\xdf\x2f\x94\x47\xf5\xa2\x23\xf2\x6e\xa2\x93\x02\x7e\xed\xd0\x7a\x8c\x72\x5a\x91\x7a\x36\x98\x7a\x88\xdc\xb0\x55\xee\xf0\x8e\x4c\x03\x78\x1f\x21\x0e\x51\x2e\x93\xc0\x6b\x9b\xbe\x6f\x83\x71\xb4\x4a\xeb\xda\x8a\x8c\x93\xce\x50\xfd\x0c\x67\x5f\xfb\x1e\x9c\x9e\x0a\x67\xfc\x3f\x87\xe0\xc3\xbf\xa4\xaf\x95\x3b\x42\xf8\x1f\xe1\xcf\x51\xfd\x35\xfa\x25\xe3\xb5\xa9\x6f\x17\x53\x2f\xe9\x47\xda\x13\xfd\xdd\x33\xfd\xdd\x6f\xe8\xc7\x19\xc1\xb2\xf1\x99\x69\xa8\x54\x67\x69\x0d\xe9\xb2\x22\x1b\x0e\xb5\x71\xc7\xe1\xa4\xbf\x18\xf2\xd7\x15\xc3\xf9\x5f\x91\x4e\x0c\x08\x9a\xd6\x2a\x02\xc6\x55\x47\x75\xe1\x87\xbb\x51\x2c\x70\xae\x20\x7d\x13\xe5\xeb\x6b\xba\x81\x64\xfa\xe7\xd8\x6c\x7e\x04\x00\x00\xff\xff\x06\xe2\x8e\xc9\x43\x06\x00\x00")
func layout_tpl_bytes() ([]byte, error) {
return bindata_read(
_layout_tpl,
"layout.tpl",
)
}
func layout_tpl() (*asset, error) {
bytes, err := layout_tpl_bytes()
if err != nil {
return nil, err
}
info := bindata_file_info{name: "layout.tpl", size: 1603, mode: os.FileMode(438), modTime: time.Unix(1423465727, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _layoutemail_tpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xb2\x49\xcd\xb5\xab\xae\x2e\x49\xcd\x2d\xc8\x49\x2c\x49\x55\x50\x4a\x2c\x2d\xc9\x48\xca\x2f\x2e\x56\x52\xd0\xab\xad\xb5\xd1\x07\xca\x02\x02\x00\x00\xff\xff\x3a\xdb\x96\xd1\x22\x00\x00\x00")
func layoutemail_tpl_bytes() ([]byte, error) {
return bindata_read(
_layoutemail_tpl,
"layoutEmail.tpl",
)
}
func layoutemail_tpl() (*asset, error) {
bytes, err := layoutemail_tpl_bytes()
if err != nil {
return nil, err
}
info := bindata_file_info{name: "layoutEmail.tpl", size: 34, mode: os.FileMode(438), modTime: time.Unix(1423465727, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _login_tpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xcc\x94\xcd\x6e\xdc\x20\x10\xc7\xef\x95\xfa\x0e\x88\xbb\xe3\x17\xb0\x2d\xf5\xd0\x9e\xfa\x11\x65\x53\xa9\x57\x0c\xb3\x01\x2d\x30\x08\xc3\x26\x91\xb5\xef\x5e\x58\x7f\x14\x6f\x36\xdb\x6b\x2c\x21\x8d\xc6\xf3\xff\x7b\xe6\x07\xb8\xd9\xa3\x37\x84\xf1\xa0\xd0\xb6\xb4\xd6\xf8\xa4\x2c\x25\x06\x82\x44\xd1\xd2\xfb\x5f\xbb\x47\xda\x7d\xfe\x44\xd2\xd3\x08\x75\x24\x5c\xb3\x61\x68\x69\x16\x55\x4f\x1e\xa3\x1b\x47\xb5\x27\x77\x5f\xbd\x47\x7f\x3a\x11\xc9\x86\x0a\x72\x3c\x8e\x60\xc5\xe9\xb4\x68\x2f\xf5\xca\xba\x18\x26\x83\xb2\xe4\x5c\x36\x38\x66\xaf\xd4\x55\x4c\x08\xb4\xb4\x6b\xd4\xda\x04\x23\x7b\x56\xc5\x01\x7c\xca\xd6\x2a\xad\x2c\xbd\xb4\x3b\x5b\x90\xf0\xea\xa0\xa5\x01\x5e\x02\xdd\xcc\xc0\xd1\x06\x8f\x9a\x12\xcb\x4c\x2a\xc8\x66\x39\xa2\xc4\x69\xc6\x41\xa2\x16\xe0\x5b\xfa\x7b\x4d\x1f\x99\x8e\xa9\x6e\x1c\xef\x96\xdc\xc5\x90\x75\x9a\x72\x01\x56\xc6\x1f\x13\x9e\x46\x7e\xf8\x3f\xbc\x99\x9e\x4b\xc2\x67\xf4\xe2\x26\xc1\x7f\x45\x1b\x82\xf7\x4b\xfa\x1d\x56\x6f\x9a\x97\xa0\x5d\xd5\x4f\xfd\x25\xda\x33\xa3\x4d\x97\xa5\x7e\x22\xb9\x93\xf8\xfc\x00\x06\x4c\x0f\xa9\xf8\x2d\x79\x2e\x81\x1f\x7a\x7c\xd9\x74\xa1\x59\x0f\xfa\xd6\xa9\x59\x55\xf3\x88\xde\xac\xe7\x20\xf8\x08\xb4\x23\xcb\x37\xc9\x0f\x28\xc7\x2b\x9d\xb7\xcd\x9e\xf7\x77\x7e\xd1\xc7\x10\x70\x9d\xbb\x0f\x96\xa4\x55\x39\xaf\x0c\xf3\xaf\xe7\x78\xc2\x30\x77\x33\xc4\xde\xa8\x40\xbb\xef\xf9\xa6\x36\xf5\xa4\xbe\x42\x81\xe3\xb1\x80\xc0\x2e\xfd\xb5\xb2\x87\x77\xcd\x89\xf4\xb0\x4f\x7f\x03\x3f\xb9\xd0\x6e\xb6\x23\x5f\x38\xc7\x68\x43\x53\xb3\xab\xa3\x94\xd4\xa4\x12\x02\xec\xc2\x2c\x6d\xe1\x9f\xdd\xc3\xb7\x9f\xd3\x85\x29\xae\x51\xce\x3e\xe2\x01\x6c\x4e\xd7\xc9\xb5\xa9\xf3\xb1\xea\xfe\x06\x00\x00\xff\xff\xb5\x6b\x5e\x3d\x98\x04\x00\x00")
var _login_tpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x7c\x52\x5b\x6e\x83\x30\x10\xfc\xaf\xd4\x3b\xac\xf6\xbf\xe5\x02\x80\xd4\xff\x3e\xa2\x36\x3d\x80\x31\x4b\x41\xc1\x5e\xb4\xd8\x49\x2a\xc4\xdd\x6b\x07\x9c\x87\x14\x95\xaf\xd5\xec\x30\x33\xcc\x92\x37\x2c\x06\x94\x76\x1d\xdb\x02\xb3\x9e\x7f\x3a\x8b\x60\xc8\xb5\x5c\x17\xb8\xf9\xf8\xda\x62\xf9\xf8\x00\xe1\x99\xa6\xae\x81\x67\x12\x61\x99\xe7\x69\x4a\x53\x5e\x09\x64\xe5\x34\x91\xad\xe7\x79\x61\xe6\x9d\x1d\xbc\x03\xf7\x3b\x50\x81\x8e\x8e\x0e\x41\xf7\x6a\x1c\x0b\x8c\x66\x4f\x9a\xad\x13\xee\x11\xac\x32\x81\xe0\x47\x92\x38\x21\x0c\xbd\xd2\xd4\x72\x5f\x93\x14\xf8\x7d\x86\xf7\xaa\xf7\x81\x17\x2c\x13\x75\x9e\x53\xa8\xd5\x6a\xf5\x1a\x82\xc9\x81\xa5\xfe\xd7\xef\x42\xba\xf1\xdb\x24\xb8\xbc\xf3\x11\x6d\x57\xd7\x64\x93\x42\x48\x72\x1c\xa5\x79\x5f\x92\x5c\xe5\x8b\xe8\x96\x77\x64\x23\x9c\xdd\xf4\x36\xb6\x7c\xf8\x24\x43\xa6\xa2\x58\xda\xb5\xb8\x6e\x49\xef\x2a\x3e\x26\x79\x31\x67\x4d\x27\x9e\xb0\x84\xf4\x22\xbc\xd1\x6d\xd1\xa7\xee\xd3\xec\x9d\x63\xbb\x6a\x8e\xbe\x32\x9d\xc3\xf2\x35\xde\x33\xcf\x96\xdd\x9d\x40\x9a\xf7\xa7\x3c\x0a\x5a\xa1\x26\xfc\x00\xb2\x40\x58\xae\x3b\x78\xd1\x9a\xbd\x75\x79\xa6\x2e\x47\xce\xb3\x58\x6c\xf9\x17\x00\x00\xff\xff\x1e\x0e\xca\x3a\x3c\x02\x00\x00")
func login_tpl_bytes() ([]byte, error) {
return bindata_read(
@ -156,7 +116,7 @@ func login_tpl() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "login.tpl", size: 1176, mode: os.FileMode(438), modTime: time.Unix(1424065052, 0)}
info := bindata_file_info{name: "login.tpl", size: 572, mode: os.FileMode(438), modTime: time.Unix(1424503580, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -176,7 +136,7 @@ func recover_complete_tpl() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "recover-complete.tpl", size: 1235, mode: os.FileMode(438), modTime: time.Unix(1424063388, 0)}
info := bindata_file_info{name: "recover-complete.tpl", size: 1235, mode: os.FileMode(438), modTime: time.Unix(1424471280, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -196,7 +156,7 @@ func recover_html_email() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "recover-html.email", size: 26, mode: os.FileMode(438), modTime: time.Unix(1422773459, 0)}
info := bindata_file_info{name: "recover-html.email", size: 26, mode: os.FileMode(438), modTime: time.Unix(1424471280, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -216,7 +176,7 @@ func recover_text_email() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "recover-text.email", size: 9, mode: os.FileMode(438), modTime: time.Unix(1422773459, 0)}
info := bindata_file_info{name: "recover-text.email", size: 9, mode: os.FileMode(438), modTime: time.Unix(1424471280, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -236,7 +196,7 @@ func recover_tpl() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "recover.tpl", size: 1282, mode: os.FileMode(438), modTime: time.Unix(1424063407, 0)}
info := bindata_file_info{name: "recover.tpl", size: 1282, mode: os.FileMode(438), modTime: time.Unix(1424471280, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -284,8 +244,6 @@ func AssetNames() []string {
var _bindata = map[string]func() (*asset, error){
"confirm_email.html.tpl": confirm_email_html_tpl,
"confirm_email.txt.tpl": confirm_email_txt_tpl,
"layout.tpl": layout_tpl,
"layoutEmail.tpl": layoutemail_tpl,
"login.tpl": login_tpl,
"recover-complete.tpl": recover_complete_tpl,
"recover-html.email": recover_html_email,
@ -337,10 +295,6 @@ var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
}},
"confirm_email.txt.tpl": &_bintree_t{confirm_email_txt_tpl, map[string]*_bintree_t{
}},
"layout.tpl": &_bintree_t{layout_tpl, map[string]*_bintree_t{
}},
"layoutEmail.tpl": &_bintree_t{layoutemail_tpl, map[string]*_bintree_t{
}},
"login.tpl": &_bintree_t{login_tpl, map[string]*_bintree_t{
}},
"recover-complete.tpl": &_bintree_t{recover_complete_tpl, map[string]*_bintree_t{

View File

@ -14,7 +14,6 @@ import (
"path/filepath"
"gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/views"
)
var (
@ -63,7 +62,7 @@ func LoadTemplates(layout *template.Template, path string, files ...string) (Tem
func (t Templates) Render(ctx *authboss.Context, w http.ResponseWriter, r *http.Request, name string, data authboss.HTMLData) error {
tpl, ok := t[name]
if !ok {
return authboss.RenderErr{tpl.Name(), data, views.ErrTemplateNotFound}
return authboss.RenderErr{tpl.Name(), data, ErrTemplateNotFound}
}
data.MergeKV("xsrfName", template.HTML(authboss.Cfg.XSRFName), "xsrfToken", template.HTML(authboss.Cfg.XSRFMaker(w, r)))

View File

@ -1,44 +0,0 @@
<html>
<head>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js" type="text/javascript"></script>
</head>
<body>
<div class="container-fluid">
{{if .FlashSuccess}}
<div class="row">
<div class="col-xs-offset-3 col-md-6">
<div class="alert alert-success alert-dismissable" style="margin-top: 75px;">
<button type="button" class="close" data-dismiss="alert"><span>&times;</span></button>
{{print .FlashSuccess}}
</div>
</div>
</div>
{{end}}
{{if .FlashError}}
<div class="row">
<div class="col-xs-offset-3 col-md-6">
<div class="alert alert-danger alert-dismissable" style="margin-top: 75px;">
<button type="button" class="close" data-dismiss="alert"><span>&times;</span></button>
{{print .FlashError}}
</div>
</div>
</div>
{{end}}
<div class="row" style="margin-top: 25px;">
<div class="col-md-offset-4 col-md-4">
<div class="panel panel-default">
<div class="panel-heading"></div>
<div class="panel-body">
{{template "authboss" .}}
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1 +0,0 @@
<em>{{template "authboss" .}}</em>

View File

@ -1,27 +1,10 @@
<form action="/login" method="POST">
<div class="form-group{{if .Error}} has-error{{end}}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user"></i></span>
<input type="text" class="form-control" name="username" placeholder="Username" value="{{.Username}}">
</div>
</div>
<div class="form-group{{if .Error}} has-error{{end}}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-lock"></i></span>
<input type="password" class="form-control" name="password" placeholder="Password">
</div>
<span class="help-block">{{.Error}}</span>
</div>
{{if .ShowRemember}}
<div class="checkbox">
<label>
<input type="checkbox" name="rm" value="true"> Remember Me
</label>
</div>
{{end}}
<button class="btn btn-primary btn-block" type="submit">Login</button>
{{if .ShowRecover}}
<a class="btn btn-link btn-block" type="submit" href="/recover">Recover Account</a>
{{end}}
<input type="hidden" name="{{.XSRFName}}" value="{{.XSRFToken}}" />
{{if .error}}{{.error}}<br />{{end}}
<input type="text" class="form-control" name="username" placeholder="Username" value="{{.username}}">
<input type="password" class="form-control" name="password" placeholder="Password">
<input type="hidden" name="{{.xsrfName}}" value="{{.xsrfToken}}" />
{{if .showRemember}}<input type="checkbox" name="rm" value="true"> Remember Me{{end}}
<br />
<button type="submit">Login</button>
{{if .showRecover}}<a href="/recover">Recover Account</a>{{end}}
</form>

View File

@ -61,5 +61,7 @@ func (c contextRoute) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, e.Endpoint, http.StatusTemporaryRedirect)
case RenderErr:
w.WriteHeader(http.StatusInternalServerError)
default:
w.WriteHeader(http.StatusInternalServerError)
}
}