Added test suites for all current models

This commit is contained in:
🐙PiperYxzzy
2022-05-01 20:49:03 +02:00
parent 5f85a5800e
commit 6e7b30be0a
12 changed files with 400 additions and 27 deletions

View File

@@ -16,7 +16,7 @@ type Admin struct {
const adminJwtDuration = time.Hour * 2
var adminHmac = util.GenerateHmac()
var AdminHmac = util.GenerateHmac()
func (a *Admin) GetJwt() (string, int) {
j := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
@@ -26,7 +26,7 @@ func (a *Admin) GetJwt() (string, int) {
"role": "admin",
})
jstr, err := j.SignedString(adminHmac)
jstr, err := j.SignedString(AdminHmac)
if err != nil {
// we should ALWAYS be able to build and sign a str
panic(err)

41
models/admin_test.go Normal file
View File

@@ -0,0 +1,41 @@
package models
import (
"testing"
"time"
"github.com/google/uuid"
"github.com/yxzzy-wtf/gin-gonic-prepack/util"
)
func TestAdminGetJwt(t *testing.T) {
a := Admin{}
a.Uid = uuid.New()
jwtToken, maxAge := a.GetJwt()
if maxAge != int(time.Hour.Seconds()*2) {
t.Errorf("issued token with incorrect max age, expected %vs but was %vs", time.Hour.Seconds()*2, maxAge)
}
testClaims, err := util.ParseJwt(jwtToken, AdminHmac)
if err != nil {
t.Errorf("tried to parse valid token but got error %v", err)
}
if testClaims["sub"] != a.Uid.String() {
t.Errorf("`sub` value of %v does not match expected of %v", testClaims["sub"], a.Uid)
}
if testClaims["role"] != "admin" {
t.Errorf("`role` value of %v does not match expected of `admin`", testClaims["role"])
}
if _, exists := testClaims["iat"]; !exists {
t.Errorf("`iat` does not exist in jwt")
}
if _, exists := testClaims["exp"]; !exists {
t.Errorf("`exp` does not exist in jwt")
}
}

View File

@@ -2,8 +2,10 @@ package models
import (
"errors"
"strings"
"time"
"github.com/pquerna/otp/totp"
"golang.org/x/crypto/bcrypt"
)
@@ -16,17 +18,29 @@ type Auth struct {
}
func (a *Auth) SetPassword(pass string) error {
if len(pass) < 12 {
return errors.New("password too short")
}
if strings.Contains(strings.ToLower(pass), "password") {
return errors.New("contains phrase 'password'")
}
passHash, _ := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
a.PasswordHash = string(passHash)
return nil
}
func (a *Auth) Login(pass string, tfCode string) (error, bool) {
return a.login(pass, tfCode, time.Now())
}
func (a *Auth) login(pass string, tfCode string, stamp time.Time) (error, bool) {
if err := a.CheckPassword(pass); err != nil {
return err, false
}
if err := a.ValidateTwoFactor(tfCode, time.Now()); err != nil {
if err := a.ValidateTwoFactor(tfCode, stamp); err != nil {
return err, true
}
@@ -51,7 +65,14 @@ func (a *Auth) ValidateTwoFactor(tfCode string, stamp time.Time) error {
//TODO two factor
if len(tfCode) == 6 {
// Test 2FA
return errors.New("2FA invalid")
expect, err := totp.GenerateCode(a.TwoFactorSecret, stamp)
if err != nil {
return errors.New("could not process 2fa")
}
if expect == tfCode {
return nil
}
return errors.New("2fa invalid")
} else {
// May be a renewal code
return errors.New("unlock invalid")

137
models/auth_test.go Normal file
View File

@@ -0,0 +1,137 @@
package models
import (
"testing"
"time"
)
func TestBadPasswords(t *testing.T) {
a := Auth{}
if err := a.SetPassword("short"); err.Error() != "password too short" {
t.Errorf("allowed short password")
}
if err := a.SetPassword("tqr9wyfPassword9k8rwcd"); err.Error() != "contains phrase 'password'" {
t.Errorf("allowed password containing the word 'password'")
}
if err := a.SetPassword("qc2q2fn34dqifqu23j7dp0"); err != nil {
t.Errorf("rejected acceptable password")
}
}
func TestSettingPassword(t *testing.T) {
a := Auth{}
if a.PasswordHash != "" {
t.Errorf("passwordhash comes with default value")
}
a.SetPassword("This-q2o37rcfy2ij34tgjwi374f3w")
ph := a.PasswordHash
if ph == "" {
t.Errorf("passwordhash was not set")
}
a.SetPassword("Different-q2o37rcfy2ij34tgjwi374f3w")
if ph == a.PasswordHash {
t.Errorf("password hashes are the same across different passwords")
}
}
func TestPasswordFlow(t *testing.T) {
a := Auth{}
a.SetPassword("Base-w894t7yw9xj8fxh834dr32")
if err := a.CheckPassword("Incorrect-w894t7yw9xj8fxh834dr32"); err == nil {
t.Errorf("did not fail when provided the wrong password")
}
if err := a.CheckPassword("Base-w894t7yw9xj8fxh834dr32"); err != nil {
t.Errorf("failed when provided the right password")
}
a.SetPassword("Secondary-w894t7yw9xj8fxh834dr32")
if err := a.CheckPassword("Base-w894t7yw9xj8fxh834dr32"); err == nil {
t.Errorf("did not fail when provided the original password")
}
if err := a.CheckPassword("Secondary-w894t7yw9xj8fxh834dr32"); err != nil {
t.Errorf("failed when provided the correct updated password")
}
}
func TestTwoFactorWhenNotSet(t *testing.T) {
a := Auth{}
if err := a.ValidateTwoFactor("ZZZZZZ", time.Now()); err == nil {
t.Errorf("no 2fa set up but code provided, should get err")
}
if err := a.ValidateTwoFactor("", time.Now()); err != nil {
t.Errorf("no code give but no 2fa set up, should not have errored")
}
}
func TestTwoFactor(t *testing.T) {
a := Auth{}
a.TwoFactorSecret = "AAAAAAAAAAAAAAAA"
testTime := time.Date(2022, 1, 5, 18, 0, 0, 0, time.UTC)
expected := "566833"
if err := a.ValidateTwoFactor("000000", testTime); err == nil {
t.Errorf("accepted invalid token")
}
if err := a.ValidateTwoFactor(expected, testTime); err != nil {
t.Errorf("rejected expected token at T0 of period")
}
if err := a.ValidateTwoFactor(expected, testTime.Add(29*time.Second)); err != nil {
t.Errorf("rejected expected token at T29 of period")
}
if err := a.ValidateTwoFactor(expected, testTime.Add(35*time.Second)); err == nil {
t.Errorf("accepted valid token at T35 of period (token is from last period)")
}
}
func TestCombinedLogin(t *testing.T) {
a := Auth{}
a.SetPassword("q2ricy2rqi3c4r23rcou")
a.TwoFactorSecret = "AAAAAAAAAAAAAAAA"
testTime := time.Date(2022, 1, 5, 18, 0, 0, 0, time.UTC)
expected := "566833"
err, show := a.login("q2ricy2rqi3c4r23rcou", expected, testTime)
if err == nil || err.Error() != "not yet verified" {
t.Errorf("validated login of unverified user")
}
if !show {
t.Errorf("unverified is an acceptable message to show, did not indicate true")
}
err, show = a.login("q2ricy2rqi3c4r23rcou", "000000", testTime)
if err == nil || err.Error() != "2fa invalid" {
t.Errorf("validated incorrect 2fa code")
}
if !show {
t.Errorf("bad 2fa is an acceptable message to show, did not indicate true")
}
err, show = a.login("bad", "000000", testTime)
if err == nil {
t.Errorf("validated bad password")
}
if show {
t.Errorf("bad passwrd not an acceptable message to show, but indicated true")
}
a.Verified = true
err, _ = a.login("q2ricy2rqi3c4r23rcou", expected, testTime)
if err != nil {
t.Errorf("failed good login")
}
}

54
models/base_test.go Normal file
View File

@@ -0,0 +1,54 @@
package models
import (
"testing"
"time"
"github.com/google/uuid"
)
func TestOnCreateNewUUID(t *testing.T) {
now := time.Now()
b := Base{}
b.BeforeCreate(nil)
if b.Uid == uuid.Nil {
t.Errorf("did not generate uuid BeforeCreate")
}
if b.Created.IsZero() {
t.Errorf("did not set created time")
}
if !b.Created.After(now) {
t.Errorf("created date should be after %v, was %v", now, b.Created)
}
if !b.Updated.IsZero() {
t.Errorf("updated date already set to %v", b.Updated)
}
if !b.Deleted.IsZero() {
t.Errorf("deleted date already set to %v", b.Updated)
}
}
func TestOnSaveUpdateDate(t *testing.T) {
now := time.Now()
b := Base{}
b.BeforeSave(nil)
if !b.Updated.After(now) {
t.Errorf("updated date should be updated to after %v, is %v", now, b.Updated)
}
}
func TestDeleteSetsTime(t *testing.T) {
now := time.Now()
b := Base{}
b.Delete()
if !b.Deleted.After(now) {
t.Errorf("updated date should be updated to after %v, is %v", now, b.Updated)
}
}

11
models/tenanted_test.go Normal file
View File

@@ -0,0 +1,11 @@
package models
import "testing"
func TestCannotCreateUntenanted(t *testing.T) {
tnt := Tenanted{}
if err := tnt.BeforeCreate(nil); err == nil {
t.Errorf("allowed creation of Tenanted model without Tenant value")
}
}

95
models/user_test.go Normal file
View File

@@ -0,0 +1,95 @@
package models
import (
"testing"
"time"
"github.com/google/uuid"
"github.com/yxzzy-wtf/gin-gonic-prepack/util"
)
func TestUserGetJwt(t *testing.T) {
u := User{}
u.Uid = uuid.New()
jwtToken, maxAge := u.GetJwt()
if maxAge != int(time.Hour.Seconds()*24) {
t.Errorf("issued token with incorrect max age, expected %vs but was %vs", time.Hour.Seconds()*24, maxAge)
}
testClaims, err := util.ParseJwt(jwtToken, UserHmac)
if err != nil {
t.Errorf("tried to parse valid token but got error %v", err)
}
if testClaims["sub"] != u.Uid.String() {
t.Errorf("`sub` value of %v does not match expected of %v", testClaims["sub"], u.Uid)
}
if testClaims["role"] != "user" {
t.Errorf("`role` value of %v does not match expected of `user`", testClaims["role"])
}
if _, exists := testClaims["iat"]; !exists {
t.Errorf("`iat` does not exist in jwt")
}
if _, exists := testClaims["exp"]; !exists {
t.Errorf("`exp` does not exist in jwt")
}
}
func TestUserGetVerifyJwt(t *testing.T) {
u := User{}
u.Uid = uuid.New()
jwtToken := u.GetVerificationJwt()
testClaims, err := util.ParseJwt(jwtToken, UserHmac)
if err != nil {
t.Errorf("tried to parse valid token but got error %v", err)
}
if testClaims["sub"] != u.Uid.String() {
t.Errorf("`sub` value of %v does not match expected of %v", testClaims["sub"], u.Uid)
}
if testClaims["role"] != "verify" {
t.Errorf("`role` value of %v does not match expected of `verify`", testClaims["role"])
}
if _, exists := testClaims["iat"]; !exists {
t.Errorf("`iat` does not exist in jwt")
}
if _, exists := testClaims["exp"]; !exists {
t.Errorf("`exp` does not exist in jwt")
}
}
func TestUserGetResetJwt(t *testing.T) {
u := User{}
u.Uid = uuid.New()
jwtToken := u.GetResetPasswordJwt()
testClaims, err := util.ParseJwt(jwtToken, UserHmac)
if err != nil {
t.Errorf("tried to parse valid token but got error %v", err)
}
if testClaims["sub"] != u.Uid.String() {
t.Errorf("`sub` value of %v does not match expected of %v", testClaims["sub"], u.Uid)
}
if testClaims["role"] != "reset" {
t.Errorf("`role` value of %v does not match expected of `reset`", testClaims["role"])
}
if _, exists := testClaims["iat"]; !exists {
t.Errorf("`iat` does not exist in jwt")
}
if _, exists := testClaims["exp"]; !exists {
t.Errorf("`exp` does not exist in jwt")
}
}