Skip to content
This repository was archived by the owner on Sep 19, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 1 addition & 34 deletions api/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions api/Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@
name = "github.com/cloudfoundry-community/go-cfenv"
version = "1.14.0"

[[constraint]]
name = "github.com/go-pg/pg"
version = "6.4.9"

[[constraint]]
name = "github.com/gorilla/mux"
version = "1.1.0"
Expand Down
143 changes: 38 additions & 105 deletions api/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import (
"fmt"

"github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
)

var (
// ErrPasswordDoesNotMatch is an error when a user inputs an invalid password
ErrPasswordDoesNotMatch = errors.New("Password does not match")

// ErrAccountDoesNotExist is an error when a users account does not exist
ErrAccountDoesNotExist = errors.New("Account does not exist")

// ErrDatastoreConnection is an error when a database connection cannot be made
ErrDatastoreConnection = errors.New("Unable to connect to datastore")
)
Expand Down Expand Up @@ -45,22 +43,32 @@ const (

// Account represents a user account
type Account struct {
ID int
Username string
Firstname string
Lastname string
Token string
TokenUsed bool
Email sql.NullString
Status string
FormType string `db:"form_type"`
FormVersion string `db:"form_version"`
ExternalID string `db:"external_id"` // ExternalID is an identifier used by external systems to id applications
ID int
Username string
Email sql.NullString
Status string
FormType string `db:"form_type"`
FormVersion string `db:"form_version"`
ExternalID string `db:"external_id"` // ExternalID is an identifier used by external systems to id applications
PasswordHash sql.NullString `db:"password_hash"` // PasswordHash is only used for basic auth, never in production.
// The only way for PasswordHash to be filled out is to call simplestore.FetchAccountWithPasswordHash
}

// Helper methods for sql.NullString

// NonNullString returns a valid sql.NullString
func NonNullString(value string) sql.NullString {
return sql.NullString{Valid: true, String: value}
}

// NullString returns an invalid sql.NullString
func NullString() sql.NullString {
return sql.NullString{}
}

// validate returns nil if the account is valid or an error describing what's wrong otherwise.
// CheckIsValid returns nil if the account is valid or an error describing what's wrong otherwise.
// This is intended to ensure data integrity by not saving invalid data to the database
func (entity Account) validate() error {
func (entity Account) CheckIsValid() error {

// the required fields are Username, FormType, and FormVersion
errorString := "Account is invalid. "
Expand All @@ -84,73 +92,6 @@ func (entity Account) validate() error {

}

// Save the Account entity.
func (entity *Account) Save(context DatabaseService, account int) (int, error) {

if err := entity.validate(); err != nil {
return entity.ID, err
}

if err := context.Save(entity); err != nil {
return entity.ID, err
}

return entity.ID, nil
}

// Delete the Account entity.
func (entity *Account) Delete(context DatabaseService, account int) (int, error) {

if entity.ID != 0 {
if err := context.Delete(entity); err != nil {
return entity.ID, err
}
}

return entity.ID, nil
}

// Get the Account entity.
func (entity *Account) Get(context DatabaseService, account int) (int, error) {

if err := entity.Find(context); err != nil {
return entity.ID, err
}

return entity.ID, nil
}

// GetID returns the entity identifier.
func (entity *Account) GetID() int {
return entity.ID
}

// SetID sets the entity identifier.
func (entity *Account) SetID(id int) {
entity.ID = id
}

// Find will search for account by `username` if no identifier is present or by ID if it is.
func (entity *Account) Find(context DatabaseService) error {
if entity.ExternalID != "" {
return entity.FindByExternalID(context)
}

if entity.ID == 0 {
return context.Where(entity, "Account.username = ?", entity.Username)
}
return context.Select(entity)
}

// FindByExternalID will search for account by `request id`
func (entity *Account) FindByExternalID(context DatabaseService) error {
if entity.ExternalID == "" {
return fmt.Errorf("No request id was given")
}

return context.Where(entity, "Account.external_id = ?", entity.ExternalID)
}

// CanSubmit returns wether the account is in a valid state for submission
func (entity *Account) CanSubmit() bool {
if entity.Status == StatusSubmitted {
Expand Down Expand Up @@ -214,33 +155,25 @@ func DefaultFormVersion(formType string) (string, error) {
return versions[0], nil
}

// BasicAuthentication checks if the username and password are valid and returns the users account
func (entity *Account) BasicAuthentication(context DatabaseService, password string) error {
var basicMembership BasicAuthMembership

// Find if basic auth record exists for given account username
err := context.ColumnsWhere(&basicMembership, []string{"basic_auth_membership.*", "Account"}, "Account.username = ?", entity.Username)
// HashPassword converts a plaintext password and generates a hash
func (entity *Account) HashPassword(password string) error {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
fmt.Printf("Basic Authentication Error: [%v]\n", err)
return ErrAccountDoesNotExist
return err
}
entity.PasswordHash = NonNullString(string(hashedPassword))
return nil
}

// Check if plaintext password matches hashed password
if matches := basicMembership.PasswordMatch(password); !matches {
return ErrPasswordDoesNotMatch
// CheckPassword checks if the username and password are valid and returns the users account
func (entity *Account) CheckPassword(password string) error {
if !entity.PasswordHash.Valid {
return ErrAccountHasNoPassword
}

if basicMembership.Account != nil {
entity.ID = basicMembership.Account.ID
entity.Username = basicMembership.Account.Username
entity.Firstname = basicMembership.Account.Firstname
entity.Lastname = basicMembership.Account.Lastname
entity.Token = basicMembership.Account.Token
entity.TokenUsed = basicMembership.Account.TokenUsed
entity.Email = basicMembership.Account.Email
entity.Status = basicMembership.Account.Status
entity.FormType = basicMembership.Account.FormType
entity.FormVersion = basicMembership.Account.FormVersion
if bcrypt.CompareHashAndPassword([]byte(entity.PasswordHash.String), []byte(password)) != nil {
return ErrPasswordDoesNotMatch
}

return nil
}
57 changes: 12 additions & 45 deletions api/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@ func TestBasicAuthentication(t *testing.T) {

account := &Account{
Username: username,
Firstname: "Admin",
Lastname: "Last",
FormType: "SF86",
FormVersion: "2017-07",
}

membership := &BasicAuthMembership{
AccountID: account.ID,
Account: account,
setErr := account.HashPassword(password)
if setErr != nil {
t.Fatal(setErr)
}
membership.HashPassword(password)

if matched := membership.PasswordMatch(password); !matched {
t.Fatal("Expected password to match")
checkErr := account.CheckPassword(password)
if checkErr != nil {
t.Fatal(checkErr)
}

if matched := membership.PasswordMatch("incorrect-password"); matched {
t.Fatal("Expected incorrect password")
badErr := account.CheckPassword("incorrect-password")
if badErr != ErrPasswordDoesNotMatch {
t.Fatal("Should have not matched!", badErr)
}

}

func TestValidFormType(t *testing.T) {
Expand Down Expand Up @@ -82,7 +82,7 @@ func TestAccountUsernameValidation(t *testing.T) {
FormType: "SF86",
FormVersion: "2017-07",
}
err := account.validate()
err := account.CheckIsValid()
if err == nil {
t.Errorf("expected a Missing Username error")
t.Fail()
Expand All @@ -97,46 +97,13 @@ func TestAccountFormTypeValidation(t *testing.T) {
FormType: "Dogs",
FormVersion: "2017-07",
}
err := account.validate()
err := account.CheckIsValid()
if err == nil {
t.Errorf("expected a Known Form error")
t.Fail()
}
}

func TestGetAccountID(t *testing.T) {

accountID := 148958398
account := &Account{
Username: "glarbal@example.com",
Email: sql.NullString{},
FormType: "Dogs",
FormVersion: "2017-07",
ID: accountID,
}
if account.GetID() != accountID {
t.Errorf("incorrect account ID")
t.Fail()
}
}

func TestSetAccountID(t *testing.T) {

accountID := 148958398
account := &Account{
Username: "glarbal@example.com",
Email: sql.NullString{},
FormType: "Dogs",
FormVersion: "2017-07",
ID: 1835833,
}
account.SetID(accountID)
if account.ID != accountID {
t.Errorf("account ID set failure")
t.Fail()
}
}

func TestSubmitAccount(t *testing.T) {

account := &Account{
Expand Down
Loading