move auth related models into auth package

This commit is contained in:
dhax 2017-10-04 19:35:22 +02:00
parent 2a9667a616
commit 6b7b5f2ae9
12 changed files with 159 additions and 151 deletions

106
auth/account.go Normal file
View file

@ -0,0 +1,106 @@
package auth
import (
"net/url"
"strings"
"time"
"github.com/dhax/go-base/models"
"github.com/go-chi/jwtauth"
validation "github.com/go-ozzo/ozzo-validation"
"github.com/go-ozzo/ozzo-validation/is"
"github.com/go-pg/pg/orm"
)
// Account represents an authenticated application user
type Account struct {
ID int `json:"id"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
LastLogin time.Time `json:"last_login,omitempty"`
Email string `json:"email"`
Name string `json:"name"`
Active bool `sql:",notnull" json:"active"`
Roles []string `pg:",array" json:"roles,omitempty"`
Profile *models.Profile `json:"profile,omitempty"`
Token []*Token `json:"token,omitempty"`
}
// BeforeInsert hook executed before database insert operation.
func (a *Account) BeforeInsert(db orm.DB) error {
now := time.Now()
if a.CreatedAt.IsZero() {
a.CreatedAt = now
a.UpdatedAt = now
}
if err := a.Validate(); err != nil {
return err
}
return nil
}
// BeforeUpdate hook executed before database update operation.
func (a *Account) BeforeUpdate(db orm.DB) error {
if err := a.Validate(); err != nil {
return err
}
a.UpdatedAt = time.Now()
return nil
}
// BeforeDelete hook executed before database delete operation.
func (a *Account) BeforeDelete(db orm.DB) error {
return nil
}
// Validate validates Account struct and returns validation errors.
func (a *Account) Validate() error {
a.Email = strings.TrimSpace(a.Email)
a.Email = strings.ToLower(a.Email)
a.Name = strings.TrimSpace(a.Name)
return validation.ValidateStruct(a,
validation.Field(&a.Email, validation.Required, is.Email, is.LowerCase),
validation.Field(&a.Name, validation.Required, is.ASCII),
)
}
// CanLogin returns true if is user is allowed to login.
func (a *Account) CanLogin() bool {
return a.Active
}
// Claims returns the account's claims to be signed
func (a *Account) Claims() jwtauth.Claims {
return jwtauth.Claims{
"id": a.ID,
"sub": a.Name,
"roles": a.Roles,
}
}
// AccountFilter provides pagination and filtering options on accounts.
type AccountFilter struct {
orm.Pager
Filters url.Values
Order []string
}
// Filter applies an AccountFilter on an orm.Query.
func (f *AccountFilter) Filter(q *orm.Query) (*orm.Query, error) {
q = q.Apply(f.Pager.Paginate)
q = q.Apply(orm.URLFilters(f.Filters))
q = q.Order(f.Order...)
return q, nil
}
// NewAccountFilter returns an AccountFilter with options parsed from request url values.
func NewAccountFilter(v url.Values) AccountFilter {
var f AccountFilter
f.SetURLValues(v)
f.Filters = v
f.Order = v["order"]
return f
}

View file

@ -6,7 +6,6 @@ import (
"github.com/dhax/go-base/email"
"github.com/dhax/go-base/logging"
"github.com/dhax/go-base/models"
"github.com/go-chi/chi"
"github.com/go-chi/render"
"github.com/sirupsen/logrus"
@ -14,12 +13,12 @@ import (
// Storer defines database operations on account and token data.
type Storer interface {
GetByID(id int) (*models.Account, error)
GetByEmail(email string) (*models.Account, error)
GetByRefreshToken(token string) (*models.Account, *models.Token, error)
UpdateAccount(a *models.Account) error
SaveRefreshToken(u *models.Token) error
DeleteRefreshToken(t *models.Token) error
GetByID(id int) (*Account, error)
GetByEmail(email string) (*Account, error)
GetByRefreshToken(token string) (*Account, *Token, error)
UpdateAccount(a *Account) error
SaveRefreshToken(t *Token) error
DeleteRefreshToken(t *Token) error
PurgeExpiredToken() error
}

View file

@ -15,7 +15,6 @@ import (
uuid "github.com/satori/go.uuid"
"github.com/dhax/go-base/email"
"github.com/dhax/go-base/models"
)
// The list of error types presented to the end user as error message.
@ -129,7 +128,7 @@ func (rs *Resource) token(w http.ResponseWriter, r *http.Request) {
ua := user_agent.New(r.UserAgent())
browser, _ := ua.Browser()
token := &models.Token{
token := &Token{
Token: uuid.NewV4().String(),
Expiry: time.Now().Add(time.Minute * rs.Token.jwtRefreshExpiry),
UpdatedAt: time.Now(),

View file

@ -15,7 +15,6 @@ import (
"github.com/dhax/go-base/email"
"github.com/dhax/go-base/logging"
"github.com/dhax/go-base/models"
"github.com/dhax/go-base/testing/mock"
"github.com/go-chi/chi"
"github.com/go-chi/jwtauth"
@ -24,7 +23,7 @@ import (
var (
auth *Resource
authstore mock.AuthStore
authstore MockStorer
mailer mock.Mailer
ts *httptest.Server
)
@ -53,9 +52,9 @@ func TestMain(m *testing.M) {
}
func TestAuthResource_login(t *testing.T) {
authstore.GetByEmailFn = func(email string) (*models.Account, error) {
authstore.GetByEmailFn = func(email string) (*Account, error) {
var err error
a := models.Account{
a := Account{
ID: 1,
Email: email,
Name: "test",
@ -115,9 +114,9 @@ func TestAuthResource_login(t *testing.T) {
}
func TestAuthResource_token(t *testing.T) {
authstore.GetByIDFn = func(id int) (*models.Account, error) {
authstore.GetByIDFn = func(id int) (*Account, error) {
var err error
a := models.Account{
a := Account{
ID: id,
Active: true,
Name: "test",
@ -132,11 +131,11 @@ func TestAuthResource_token(t *testing.T) {
}
return &a, err
}
authstore.UpdateAccountFn = func(a *models.Account) error {
authstore.UpdateAccountFn = func(a *Account) error {
a.LastLogin = time.Now()
return nil
}
authstore.SaveRefreshTokenFn = func(a *models.Token) error {
authstore.SaveRefreshTokenFn = func(a *Token) error {
return nil
}
@ -185,13 +184,13 @@ func TestAuthResource_token(t *testing.T) {
}
func TestAuthResource_refresh(t *testing.T) {
authstore.GetByRefreshTokenFn = func(token string) (*models.Account, *models.Token, error) {
authstore.GetByRefreshTokenFn = func(token string) (*Account, *Token, error) {
var err error
a := models.Account{
a := Account{
Active: true,
Name: "Test",
}
var t models.Token
var t Token
t.Expiry = time.Now().Add(1 * time.Minute)
switch token {
@ -206,14 +205,14 @@ func TestAuthResource_refresh(t *testing.T) {
}
return &a, &t, err
}
authstore.UpdateAccountFn = func(a *models.Account) error {
authstore.UpdateAccountFn = func(a *Account) error {
a.LastLogin = time.Now()
return nil
}
authstore.SaveRefreshTokenFn = func(a *models.Token) error {
authstore.SaveRefreshTokenFn = func(a *Token) error {
return nil
}
authstore.DeleteRefreshTokenFn = func(t *models.Token) error {
authstore.DeleteRefreshTokenFn = func(t *Token) error {
return nil
}
@ -260,10 +259,10 @@ func TestAuthResource_refresh(t *testing.T) {
}
func TestAuthResource_logout(t *testing.T) {
authstore.GetByRefreshTokenFn = func(token string) (*models.Account, *models.Token, error) {
authstore.GetByRefreshTokenFn = func(token string) (*Account, *Token, error) {
var err error
var a models.Account
t := models.Token{
var a Account
t := Token{
Expiry: time.Now().Add(1 * time.Minute),
}
@ -273,7 +272,7 @@ func TestAuthResource_logout(t *testing.T) {
}
return &a, &t, err
}
authstore.DeleteRefreshTokenFn = func(a *models.Token) error {
authstore.DeleteRefreshTokenFn = func(a *Token) error {
return nil
}

67
auth/mockStorer.go Normal file
View file

@ -0,0 +1,67 @@
package auth
// MockStorer mocks Storer interface.
type MockStorer struct {
GetByIDFn func(id int) (*Account, error)
GetByIDInvoked bool
GetByEmailFn func(email string) (*Account, error)
GetByEmailInvoked bool
GetByRefreshTokenFn func(token string) (*Account, *Token, error)
GetByRefreshTokenInvoked bool
UpdateAccountFn func(a *Account) error
UpdateAccountInvoked bool
SaveRefreshTokenFn func(t *Token) error
SaveRefreshTokenInvoked bool
DeleteRefreshTokenFn func(t *Token) error
DeleteRefreshTokenInvoked bool
PurgeExpiredTokenFn func() error
PurgeExpiredTokenInvoked bool
}
// GetByID mock returns an account by ID.
func (s *MockStorer) GetByID(id int) (*Account, error) {
s.GetByIDInvoked = true
return s.GetByIDFn(id)
}
// GetByEmail mock returns an account by email.
func (s *MockStorer) GetByEmail(email string) (*Account, error) {
s.GetByEmailInvoked = true
return s.GetByEmailFn(email)
}
// GetByRefreshToken mock returns an account and refresh token by token identifier.
func (s *MockStorer) GetByRefreshToken(token string) (*Account, *Token, error) {
s.GetByRefreshTokenInvoked = true
return s.GetByRefreshTokenFn(token)
}
// UpdateAccount mock upates account data related to authentication.
func (s *MockStorer) UpdateAccount(a *Account) error {
s.UpdateAccountInvoked = true
return s.UpdateAccountFn(a)
}
// SaveRefreshToken mock creates or updates a refresh token.
func (s *MockStorer) SaveRefreshToken(t *Token) error {
s.SaveRefreshTokenInvoked = true
return s.SaveRefreshTokenFn(t)
}
// DeleteRefreshToken mock deletes a refresh token.
func (s *MockStorer) DeleteRefreshToken(t *Token) error {
s.DeleteRefreshTokenInvoked = true
return s.DeleteRefreshTokenFn(t)
}
// PurgeExpiredToken mock deletes expired refresh token.
func (s *MockStorer) PurgeExpiredToken() error {
s.PurgeExpiredTokenInvoked = true
return s.PurgeExpiredTokenFn()
}

45
auth/token.go Normal file
View file

@ -0,0 +1,45 @@
package auth
import (
"time"
"github.com/go-chi/jwtauth"
"github.com/go-pg/pg/orm"
)
// Token holds refresh jwt information.
type Token struct {
ID int `json:"id,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
AccountID int `json:"-"`
Token string `json:"-"`
Expiry time.Time `json:"-"`
Mobile bool `sql:",notnull" json:"mobile"`
Identifier string `json:"identifier,omitempty"`
}
// BeforeInsert hook executed before database insert operation.
func (t *Token) BeforeInsert(db orm.DB) error {
now := time.Now()
if t.CreatedAt.IsZero() {
t.CreatedAt = now
t.UpdatedAt = now
}
return nil
}
// BeforeUpdate hook executed before database update operation.
func (t *Token) BeforeUpdate(db orm.DB) error {
t.UpdatedAt = time.Now()
return nil
}
// Claims returns the token claims to be signed
func (t *Token) Claims() jwtauth.Claims {
return jwtauth.Claims{
"id": t.ID,
"token": t.Token,
}
}