move auth related models into auth package
This commit is contained in:
parent
2a9667a616
commit
6b7b5f2ae9
12 changed files with 159 additions and 151 deletions
106
auth/account.go
Normal file
106
auth/account.go
Normal 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
|
||||
}
|
||||
13
auth/api.go
13
auth/api.go
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
67
auth/mockStorer.go
Normal 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
45
auth/token.go
Normal 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,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue