mirror of
https://github.com/volatiletech/authboss.git
synced 2025-04-13 11:50:27 +02:00
parent
04d2716963
commit
e83110ee31
29
README.md
29
README.md
@ -68,6 +68,7 @@ Once you've got this code set up, it's time to implement the use cases you care
|
||||
<a name="use_cases"></a>Use Cases
|
||||
=================================
|
||||
- Get the logged in user ([goto](#current_user))
|
||||
- Reset a User's password ([goto](#reset_password))
|
||||
- User authentication via password ([goto](#auth))
|
||||
- User authentication via OAuth2 ([goto](#oauth2))
|
||||
- User registration ([goto](#register))
|
||||
@ -103,6 +104,34 @@ nil, ErrUserNotFound | Session had user ID, but user not found in database.
|
||||
nil, err | Some horrible error has occurred.
|
||||
user struct, nil | The user is logged in.
|
||||
|
||||
## <a name="reset_password"></a>Reset a User's password
|
||||
|
||||
Because on password reset various cleanings need to happen (for example Remember Me tokens
|
||||
should all be deleted) setting the password yourself is not a good idea.
|
||||
|
||||
Authboss has the UpdatePassword method for you to use. Please consult it's documentation
|
||||
for a thorough explanation of each parameter.
|
||||
|
||||
```go
|
||||
func UpdatePassword(w http.ResponseWriter, r *http.Request, ptPassword string, user interface{}, updater func() error) error {
|
||||
```
|
||||
|
||||
Please read it's documentation as it's quite thorough, and example usage might be:
|
||||
|
||||
```go
|
||||
myUserSave := func() error {
|
||||
_, err := db.Exec(`update user set name = $1, password = $2 where id = $3`, user.Name, user.Password, user.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// HINT: Never pass the form value directly into the database as you see here :D
|
||||
err := UpdatePassword(w, r, r.FormValue("password"), &user1, myUserSave)
|
||||
if err != nil {
|
||||
// Handle error here, in most cases this will be the error from myUserSave
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## <a name="auth"></a>User Authentication via Password
|
||||
**Requirements:**
|
||||
- Auth module ([gopkg.in/authboss.v0/auth](https://github.com/go-authboss/authboss/tree/master/auth))
|
||||
|
68
authboss.go
68
authboss.go
@ -7,9 +7,14 @@ races without having to think about how to store passwords or remember tokens.
|
||||
package authboss // import "gopkg.in/authboss.v0"
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Init authboss and it's loaded modules.
|
||||
@ -70,3 +75,66 @@ func CurrentUserP(w http.ResponseWriter, r *http.Request) interface{} {
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
/*
|
||||
UpdatePassword should be called to recalculate hashes and do any cleanup
|
||||
that should occur on password resets. Updater should return an error if the
|
||||
update to the user failed (for reasons say like validation, duplicate
|
||||
primary key, etc...). In that case the cleanup will not be performed.
|
||||
|
||||
The w and r parameters are for establishing session and cookie storers.
|
||||
|
||||
The ptPassword parameter is for the password to update to, if it is empty then
|
||||
nothing is updated and the cleanup routines are not called.
|
||||
|
||||
The user parameter is the user struct which will have it's
|
||||
Password string/sql.NullString value set to the new bcrypted password. Therefore
|
||||
it must be passed in as a pointer with the Password field exported or an error
|
||||
will be returned.
|
||||
|
||||
The error returned is returned either from the updater if that produced an error
|
||||
or from the cleanup routines.
|
||||
*/
|
||||
func UpdatePassword(w http.ResponseWriter, r *http.Request,
|
||||
ptPassword string, user interface{}, updater func() error) error {
|
||||
|
||||
updatePwd := len(ptPassword) > 0
|
||||
|
||||
if updatePwd {
|
||||
pass, err := bcrypt.GenerateFromPassword([]byte(ptPassword), Cfg.BCryptCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(user).Elem()
|
||||
field := val.FieldByName("Password")
|
||||
if !field.CanSet() {
|
||||
return errors.New("authboss: UpdatePassword called without a modifyable user struct")
|
||||
}
|
||||
fieldPtr := field.Addr()
|
||||
|
||||
if scanner, ok := fieldPtr.Interface().(sql.Scanner); ok {
|
||||
if err := scanner.Scan(string(pass)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
field.SetString(string(pass))
|
||||
}
|
||||
}
|
||||
|
||||
if err := updater(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !updatePwd {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, err := ContextFromRequest(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.SessionStorer = clientStoreWrapper{Cfg.SessionStoreMaker(w, r)}
|
||||
ctx.CookieStorer = clientStoreWrapper{Cfg.CookieStoreMaker(w, r)}
|
||||
return Cfg.Callbacks.FireAfter(EventPasswordReset, ctx)
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package authboss
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@ -15,7 +17,7 @@ func TestMain(main *testing.M) {
|
||||
}
|
||||
|
||||
func TestAuthBossInit(t *testing.T) {
|
||||
NewConfig()
|
||||
Cfg = NewConfig()
|
||||
err := Init()
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err)
|
||||
@ -23,7 +25,7 @@ func TestAuthBossInit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthBossCurrentUser(t *testing.T) {
|
||||
NewConfig()
|
||||
Cfg = NewConfig()
|
||||
Cfg.Storer = mockStorer{"joe": Attributes{"email": "john@john.com", "password": "lies"}}
|
||||
Cfg.SessionStoreMaker = func(_ http.ResponseWriter, _ *http.Request) ClientStorer {
|
||||
return mockClientStore{SessionKey: "joe"}
|
||||
@ -46,3 +48,82 @@ func TestAuthBossCurrentUser(t *testing.T) {
|
||||
t.Error("Wrong user found!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthbossUpdatePassword(t *testing.T) {
|
||||
Cfg = NewConfig()
|
||||
session := mockClientStore{}
|
||||
cookies := mockClientStore{}
|
||||
Cfg.SessionStoreMaker = func(_ http.ResponseWriter, _ *http.Request) ClientStorer {
|
||||
return session
|
||||
}
|
||||
Cfg.CookieStoreMaker = func(_ http.ResponseWriter, _ *http.Request) ClientStorer {
|
||||
return cookies
|
||||
}
|
||||
|
||||
called := false
|
||||
Cfg.Callbacks.After(EventPasswordReset, func(ctx *Context) error {
|
||||
called = true
|
||||
return nil
|
||||
})
|
||||
|
||||
user1 := struct {
|
||||
Password string
|
||||
}{}
|
||||
user2 := struct {
|
||||
Password sql.NullString
|
||||
}{}
|
||||
|
||||
r, _ := http.NewRequest("GET", "http://localhost", nil)
|
||||
|
||||
called = false
|
||||
err := UpdatePassword(nil, r, "newpassword", &user1, func() error { return nil })
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(user1.Password) == 0 {
|
||||
t.Error("Password not updated")
|
||||
}
|
||||
if !called {
|
||||
t.Error("Callbacks should have been called.")
|
||||
}
|
||||
|
||||
called = false
|
||||
err = UpdatePassword(nil, r, "newpassword", &user2, func() error { return nil })
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !user2.Password.Valid || len(user2.Password.String) == 0 {
|
||||
t.Error("Password not updated")
|
||||
}
|
||||
if !called {
|
||||
t.Error("Callbacks should have been called.")
|
||||
}
|
||||
|
||||
called = false
|
||||
oldPassword := user1.Password
|
||||
err = UpdatePassword(nil, r, "", &user1, func() error { return nil })
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if user1.Password != oldPassword {
|
||||
t.Error("Password not updated")
|
||||
}
|
||||
if called {
|
||||
t.Error("Callbacks should not have been called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthbossUpdatePasswordFail(t *testing.T) {
|
||||
user1 := struct {
|
||||
Password string
|
||||
}{}
|
||||
|
||||
anErr := errors.New("AnError")
|
||||
err := UpdatePassword(nil, nil, "update", &user1, func() error { return anErr })
|
||||
if err != anErr {
|
||||
t.Error("Expected an specific error:", err)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user