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

Start the register module.

This commit is contained in:
Aaron 2015-02-23 02:03:29 -08:00
parent 73dcb5beb1
commit 6f074543f4
7 changed files with 239 additions and 16 deletions

View File

@ -5,7 +5,6 @@ import (
"io"
"io/ioutil"
"net/smtp"
"strings"
"time"
"golang.org/x/crypto/bcrypt"
@ -105,8 +104,8 @@ func NewConfig() *Config {
},
},
ConfirmFields: []string{
StoreEmail, "confirm" + strings.Title(StoreEmail),
StorePassword, "confirm" + strings.Title(StorePassword),
StoreEmail, ConfirmPrefix + StoreEmail,
StorePassword, ConfirmPrefix + StorePassword,
},
RecoverRedirect: "/login",

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(1424471280, 0)}
info := bindata_file_info{name: "confirm_email.html.tpl", size: 26, mode: os.FileMode(438), modTime: time.Unix(1424498554, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -96,12 +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(1424471280, 0)}
info := bindata_file_info{name: "confirm_email.txt.tpl", size: 9, mode: os.FileMode(438), modTime: time.Unix(1424498554, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
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")
var _login_tpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x7c\x92\x4d\x6e\x83\x30\x10\x85\xf7\x95\x7a\x87\xd1\xec\x5b\x2e\x00\x96\xba\xef\x4f\xd4\xa6\x07\x30\x66\x28\x28\xd8\x83\x06\x3b\x49\x85\xb8\x7b\xed\x12\x12\x90\xaa\xb2\xb2\x9e\x1f\xef\x7b\xcc\x90\xd7\x2c\x16\xb4\xf1\x2d\xbb\x02\xb3\x8e\xbf\x5a\x87\x60\xc9\x37\x5c\x15\xb8\x7b\xfb\xd8\xa3\xba\xbf\x83\xf8\x8c\x63\x5b\xc3\x23\x89\xb0\x4c\xd3\x38\x2e\xa7\xbc\x14\xc8\xd4\x38\x92\xab\xa6\x69\x76\xe6\xad\xeb\x83\x07\xff\xdd\x53\x81\x9e\xce\x1e\xc1\x74\x7a\x18\x0a\x4c\xb0\x07\xc3\xce\x0b\x77\x08\x4e\xdb\x68\x08\x03\x49\x3a\x21\xf4\x9d\x36\xd4\x70\x57\x91\x14\xf8\x79\x95\x8f\xba\x0b\xd1\x17\x91\x8b\x75\x9a\x50\xcd\xdc\x0d\xf0\x42\xec\x23\xea\xc4\x52\xfd\x4b\xbd\x99\x36\xd4\xdd\x22\xff\x95\x3f\xc7\x37\x6d\x55\x91\x5b\x72\x62\xab\xf3\x20\xf5\xeb\xdc\x6a\xd5\x35\xa9\x7b\x3e\x90\x4b\x72\xb6\x99\xe1\xd0\xf0\xe9\x9d\x2c\xd9\x92\xd2\x00\xd7\xe1\xa6\x21\x73\x28\xf9\xbc\xc4\x8b\xbd\x66\x7a\x09\x84\x0a\x96\x17\xe1\x85\xb6\x43\x5f\xf7\x2d\x83\xf7\xec\x2e\x99\x43\x28\x6d\xeb\x51\x3d\xa7\xdd\xe6\xd9\x7c\xb7\xf9\xbe\x75\x2d\xc3\xc7\xdf\x56\x1a\x1a\xa1\x3a\xfe\x12\x32\x4b\xa8\x2e\x77\xf0\x64\x0c\x07\xe7\xf3\x4c\xdf\xd6\x9e\x67\x69\xc8\xea\x27\x00\x00\xff\xff\xe8\x9d\xff\x88\x4e\x02\x00\x00")
func login_tpl_bytes() ([]byte, error) {
return bindata_read(
@ -116,7 +116,7 @@ func login_tpl() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "login.tpl", size: 572, mode: os.FileMode(438), modTime: time.Unix(1424503580, 0)}
info := bindata_file_info{name: "login.tpl", size: 590, mode: os.FileMode(438), modTime: time.Unix(1424567032, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -136,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(1424471280, 0)}
info := bindata_file_info{name: "recover-complete.tpl", size: 1235, mode: os.FileMode(438), modTime: time.Unix(1424498554, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -156,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(1424471280, 0)}
info := bindata_file_info{name: "recover-html.email", size: 26, mode: os.FileMode(438), modTime: time.Unix(1424498554, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -176,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(1424471280, 0)}
info := bindata_file_info{name: "recover-text.email", size: 9, mode: os.FileMode(438), modTime: time.Unix(1424498554, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -196,7 +196,27 @@ func recover_tpl() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "recover.tpl", size: 1623, mode: os.FileMode(438), modTime: time.Unix(1424541421, 0)}
info := bindata_file_info{name: "recover.tpl", size: 1623, mode: os.FileMode(438), modTime: time.Unix(1424567032, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _register_html_tpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x93\x41\x6f\xea\x30\x0c\xc7\xcf\xf4\x53\x58\x11\xe7\xf6\x8e\xd2\x5e\xde\xbb\x3c\xe9\x69\xe2\x30\xed\x3a\x85\xc6\xa5\xd1\xd2\x24\x4a\xc2\x00\x55\xfd\xee\x4b\x68\xd7\x12\xd0\x36\x76\x81\xd8\xfa\xdb\xfe\xff\x2c\x97\x36\xda\x76\xc0\x6a\x2f\xb4\x2a\x89\xc5\xbd\x70\x1e\x2d\x81\x0e\x7d\xab\x79\x49\x8c\x76\x9e\x54\xd9\x8a\x4a\xb6\x43\x09\x41\x5d\x92\xbe\xcf\x8d\x15\x1d\xb3\xe7\x7f\x7f\x87\x81\x54\x69\xbc\xa1\xc5\x45\x1b\x8b\x84\x32\x07\x0f\x8a\x75\x58\xa6\x22\xf0\x67\x83\x25\xf1\x78\xf2\x04\xde\x99\x3c\x60\x6c\x2b\x1a\x58\x44\x2f\x31\x3b\x0c\xd7\x75\x73\x0a\x15\x0f\x83\xc1\x48\x56\x63\xab\x25\xc7\x7b\x57\x50\x54\x74\x67\xc3\x6f\xb6\xea\xfb\xb5\x11\x1c\x36\x25\x5c\x2b\xfa\xfe\x28\x7c\x0b\x39\x5a\xeb\xe6\x68\x1d\x22\x19\x56\x10\xc5\x42\x71\x3c\x41\x0e\xb1\x38\x0a\x2c\x53\x7b\x9c\x15\xc3\x40\x9d\x61\x2a\xc2\x87\x67\x71\x79\x8f\x03\x27\x7b\xe9\x5f\xba\x41\xc3\x9c\x3b\x6a\xcb\x49\xb5\x9d\x5e\x5f\x6c\x6d\x51\x4e\x1b\x5b\xe2\x04\x7e\x3b\xa7\xaf\xb1\x53\xc0\xd1\x7f\xfe\xd9\xe1\x51\x80\xd4\x79\xad\x55\x23\x6c\xf7\xba\x10\xfc\x19\x33\xf0\x13\xc9\x5d\xe5\xf7\x44\xb7\x6d\x1f\x20\xbb\x9d\xf0\x0b\xc2\xd1\xe8\x68\xc8\x1d\x76\x9d\x58\xce\xf2\xbf\xde\x0b\x45\xe6\xd9\x94\x41\x6b\xb1\x29\x49\x11\xd0\x99\xaa\x51\xd2\x82\x55\xd9\x4d\x8f\x56\x70\x8e\x8a\x4c\xe8\xc1\xc0\xc9\xd9\xe6\x29\x04\xf1\x32\xe7\x7b\xbf\x64\x9f\xf5\x1b\xaa\xf1\x60\x33\x5a\xc4\xcf\xb1\xfa\x08\x00\x00\xff\xff\x3c\x36\x7b\x13\x95\x03\x00\x00")
func register_html_tpl_bytes() ([]byte, error) {
return bindata_read(
_register_html_tpl,
"register.html.tpl",
)
}
func register_html_tpl() (*asset, error) {
bytes, err := register_html_tpl_bytes()
if err != nil {
return nil, err
}
info := bindata_file_info{name: "register.html.tpl", size: 917, mode: os.FileMode(438), modTime: time.Unix(1424682732, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -249,6 +269,7 @@ var _bindata = map[string]func() (*asset, error){
"recover-html.email": recover_html_email,
"recover-text.email": recover_text_email,
"recover.tpl": recover_tpl,
"register.html.tpl": register_html_tpl,
}
// AssetDir returns the file names below a certain
@ -305,6 +326,8 @@ var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
}},
"recover.tpl": &_bintree_t{recover_tpl, map[string]*_bintree_t{
}},
"register.html.tpl": &_bintree_t{register_html_tpl, map[string]*_bintree_t{
}},
}}
// Restore an asset under the given directory

View File

@ -12,6 +12,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"gopkg.in/authboss.v0"
)
@ -19,6 +20,10 @@ import (
var (
// ErrTemplateNotFound should be returned from Get when the view is not found
ErrTemplateNotFound = errors.New("Template not found")
funcMap = template.FuncMap{
"title": strings.Title,
}
)
// Templates is a map depicting the forms a template needs wrapped within the specified layout
@ -47,7 +52,7 @@ func LoadTemplates(layout *template.Template, path string, files ...string) (Tem
return nil, err
}
_, err = clone.New("authboss").Parse(string(b))
_, err = clone.New("authboss").Funcs(funcMap).Parse(string(b))
if err != nil {
return nil, err
}
@ -71,12 +76,12 @@ func (t Templates) Render(ctx *authboss.Context, w http.ResponseWriter, r *http.
data.Merge(authboss.Cfg.LayoutDataMaker(w, r))
}
if flash, ok := ctx.CookieStorer.Get(authboss.FlashSuccessKey); ok {
ctx.CookieStorer.Del(authboss.FlashSuccessKey)
if flash, ok := ctx.SessionStorer.Get(authboss.FlashSuccessKey); ok {
ctx.SessionStorer.Del(authboss.FlashSuccessKey)
data.MergeKV(authboss.FlashSuccessKey, flash)
}
if flash, ok := ctx.CookieStorer.Get(authboss.FlashErrorKey); ok {
ctx.CookieStorer.Del(authboss.FlashErrorKey)
if flash, ok := ctx.SessionStorer.Get(authboss.FlashErrorKey); ok {
ctx.SessionStorer.Del(authboss.FlashErrorKey)
data.MergeKV(authboss.FlashErrorKey, flash)
}

View File

@ -0,0 +1,15 @@
<form action="register" method="post">
<label for="{{.primaryID}}">{{.primaryID}}:</label>
<input name={{.primaryID}} type="text" value="{{if .primaryIDValue}}{{.primaryIDValue}}{{end}}" placeholder="{{.primaryID}}" /><br />
{{$pid := .primaryID}}{{with .errs}}{{with $errlist := index . $pid}}{{range $errlist}}<span>{{.}}</span><br />{{end}}{{end}}{{end}}
<label for="password">Password:</label>
<input name="password" type="password" placeholder="Password" /><br />
{{with .errs}}{{range .password}}<span>{{.}}</span><br />{{end}}{{end}}
<label for="confirm_password">Confirm Password:</label>
<input name="confirm_password" type="password" placeholder="Confirm Password" /><br />
{{with .errs}}{{range .confirm_password}}<span>{{.}}</span><br />{{end}}{{end}}
<input type="submit" value="Login"><br />
<a href="/">Cancel</a>
<input type="hidden" name="{{.xsrfName}}" value="{{.xsrfToken}}" />
</form>

107
register/register.go Normal file
View File

@ -0,0 +1,107 @@
// Package register allows for user registration.
package register
import (
"net/http"
"gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/render"
)
const (
tplRegister = "register.html.tpl"
)
// R is the singleton instance of the register module which will have been
// configured and ready to use after authboss.Init()
var R *Register
func init() {
R = &Register{}
authboss.RegisterModule("register", R)
}
// Register module.
type Register struct {
templates render.Templates
}
// Initialize the module.
func (r *Register) Initialize() (err error) {
if r.templates, err = render.LoadTemplates(authboss.Cfg.Layout, authboss.Cfg.ViewsPath, tplRegister); err != nil {
return err
}
return nil
}
// Routes creates the routing table.
func (r *Register) Routes() authboss.RouteTable {
return authboss.RouteTable{
"/register": r.registerHandler,
}
}
// Storage returns storage requirements.
func (r *Register) Storage() authboss.StorageOptions {
return authboss.StorageOptions{
authboss.Cfg.PrimaryID: authboss.String,
authboss.StorePassword: authboss.String,
}
}
func (reg *Register) registerHandler(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error {
switch r.Method {
case "GET":
data := authboss.HTMLData{
"primaryID": authboss.Cfg.PrimaryID,
"primaryIDValue": "",
}
return reg.templates.Render(ctx, w, r, tplRegister, data)
case "POST":
return reg.registerPostHandler(ctx, w, r)
}
return nil
}
func (reg *Register) registerPostHandler(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) error {
key, _ := ctx.FirstPostFormValue(authboss.Cfg.PrimaryID)
password, _ := ctx.FirstPostFormValue(authboss.StorePassword)
policies := authboss.FilterValidators(authboss.Cfg.Policies, authboss.Cfg.PrimaryID, authboss.StorePassword)
validationErrs := ctx.Validate(policies, authboss.Cfg.ConfirmFields...)
if len(validationErrs) != 0 {
data := authboss.HTMLData{
"primaryID": authboss.Cfg.PrimaryID,
"primaryIDValue": key,
"errs": validationErrs.Map(),
}
return reg.templates.Render(ctx, w, r, tplRegister, data)
}
attr, err := ctx.Attributes() // Attributes from overriden forms
if err != nil {
return err
}
attr[authboss.Cfg.PrimaryID] = key
attr[authboss.StorePassword] = password
ctx.User = attr
if err := authboss.Cfg.Storer.Create(key, attr); err != nil {
return err
}
authboss.Cfg.Callbacks.FireAfter(authboss.EventRegister, ctx)
if authboss.IsLoaded("confirm") {
render.Redirect(ctx, w, r, "/", "Account successfully created, please verify your e-mail address.", "")
return nil
}
ctx.SessionStorer.Put(authboss.SessionKey, key)
render.Redirect(ctx, w, r, "/", "Account successfully created, you are now logged in.", "")
return nil
}

69
register/register_test.go Normal file
View File

@ -0,0 +1,69 @@
package register
import (
"html/template"
"net/http"
"net/http/httptest"
"strings"
"testing"
"gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/mocks"
)
func TestRegister(t *testing.T) {
authboss.Cfg = authboss.NewConfig()
r := Register{}
if err := r.Initialize(); err != nil {
t.Error(err)
}
if r.Routes()["/register"] == nil {
t.Error("Expected a register handler at /register.")
}
sto := r.Storage()
if sto[authboss.Cfg.PrimaryID] != authboss.String {
t.Error("Wanted primary ID to be a string.")
}
if sto[authboss.StorePassword] != authboss.String {
t.Error("Wanted password to be a string.")
}
}
func TestRegisterGet(t *testing.T) {
authboss.Cfg = &authboss.Config{
Layout: template.Must(template.New("").Parse(`{{template "authboss"}}`)),
XSRFName: "xsrf",
XSRFMaker: func(_ http.ResponseWriter, _ *http.Request) string {
return "xsrfvalue"
},
}
reg := Register{}
if err := reg.Initialize(); err != nil {
t.Error(err)
}
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/register", nil)
ctx, _ := authboss.ContextFromRequest(r)
ctx.SessionStorer = mocks.NewMockClientStorer()
if err := reg.registerHandler(ctx, w, r); err != nil {
t.Error(err)
}
if w.Code != http.StatusOK {
t.Error("It should have written a 200:", w.Code)
}
if w.Body.Len() == 0 {
t.Error("It should have wrote a response.")
}
if str := w.Body.String(); !strings.Contains(str, "<form") {
t.Error("It should have rendered a nice form:", str)
}
}

View File

@ -5,6 +5,11 @@ import (
"fmt"
)
const (
// ConfirmPrefix is prepended to names of confirm fields.
ConfirmPrefix = "confirm_"
)
// Validator is anything that can validate a string and provide a list of errors
// and describe its set of rules.
type Validator interface {