Skip to content

Commit

Permalink
Merge pull request #7 from justinfarrelldev/account-password-fix
Browse files Browse the repository at this point in the history
Changed UpdateAccount to only allow updates with the right password
  • Loading branch information
justinfarrelldev authored Oct 31, 2024
2 parents 293fb34 + 8083b31 commit 46d96ac
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 77 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"gamemode",
"gofmt",
"Gopls",
"Hasher",
"healthcheck",
"healthgrpc",
"Ninjaboy",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ You absolutely can, so long as you follow the license in `LICENSE.md`. I am real
- [x] Accounts can be updated
- [ ] Accounts can be deleted
- [ ] Passwords can be reset
- [ ] Passwords can be compared to find if passwords are correct
- [x] Passwords can be compared to find if passwords are correct
- [ ] Accounts can be logged into (and will provide a valid JWT token / session for future calls) [I need to research JWTs vs session-based auth for this task]
- [ ] All account endpoints are rate-limited appropriately

Expand Down
58 changes: 40 additions & 18 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,36 @@ const docTemplate = `{
}
}
},
"account.AccountParam": {
"description": "Structure for representing a player account with non-required fields.",
"type": "object",
"properties": {
"email": {
"description": "Email is the email address of the player.",
"type": "string"
},
"experience_level": {
"description": "ExperienceLevel represents the player's experience level (0=beginner, 1=easy, 2=medium, 3=hard, 4=very hard, 5=impossible)",
"allOf": [
{
"$ref": "#/definitions/account.ExperienceLevel"
}
]
},
"info": {
"description": "Info contains additional information about the player.",
"type": "string"
},
"location": {
"description": "Location indicates the player's real-life location.",
"type": "string"
},
"name": {
"description": "Name is the name of the player.",
"type": "string"
}
}
},
"account.CreateAccountArgs": {
"description": "Structure for the account creation request payload.",
"type": "object",
Expand Down Expand Up @@ -308,28 +338,20 @@ const docTemplate = `{
"description": "Structure for the account update request payload.",
"type": "object",
"properties": {
"account": {
"description": "The account to create.",
"allOf": [
{
"$ref": "#/definitions/account.AccountParam"
}
]
},
"account_id": {
"description": "The account ID for the account that will be updated.",
"type": "integer"
},
"email": {
"description": "The new email for the account.",
"type": "string"
},
"experience_level": {
"description": "The new experience level for the account.",
"type": "integer"
},
"info": {
"description": "The new info for the account.",
"type": "string"
},
"location": {
"description": "The new location for the account.",
"type": "string"
},
"name": {
"description": "The new name for the account.",
"password": {
"description": "The password for the account to be created",
"type": "string"
}
}
Expand Down
58 changes: 40 additions & 18 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,36 @@
}
}
},
"account.AccountParam": {
"description": "Structure for representing a player account with non-required fields.",
"type": "object",
"properties": {
"email": {
"description": "Email is the email address of the player.",
"type": "string"
},
"experience_level": {
"description": "ExperienceLevel represents the player's experience level (0=beginner, 1=easy, 2=medium, 3=hard, 4=very hard, 5=impossible)",
"allOf": [
{
"$ref": "#/definitions/account.ExperienceLevel"
}
]
},
"info": {
"description": "Info contains additional information about the player.",
"type": "string"
},
"location": {
"description": "Location indicates the player's real-life location.",
"type": "string"
},
"name": {
"description": "Name is the name of the player.",
"type": "string"
}
}
},
"account.CreateAccountArgs": {
"description": "Structure for the account creation request payload.",
"type": "object",
Expand Down Expand Up @@ -299,28 +329,20 @@
"description": "Structure for the account update request payload.",
"type": "object",
"properties": {
"account": {
"description": "The account to create.",
"allOf": [
{
"$ref": "#/definitions/account.AccountParam"
}
]
},
"account_id": {
"description": "The account ID for the account that will be updated.",
"type": "integer"
},
"email": {
"description": "The new email for the account.",
"type": "string"
},
"experience_level": {
"description": "The new experience level for the account.",
"type": "integer"
},
"info": {
"description": "The new info for the account.",
"type": "string"
},
"location": {
"description": "The new location for the account.",
"type": "string"
},
"name": {
"description": "The new name for the account.",
"password": {
"description": "The password for the account to be created",
"type": "string"
}
}
Expand Down
41 changes: 27 additions & 14 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ definitions:
description: Name is the name of the player.
type: string
type: object
account.AccountParam:
description: Structure for representing a player account with non-required fields.
properties:
email:
description: Email is the email address of the player.
type: string
experience_level:
allOf:
- $ref: '#/definitions/account.ExperienceLevel'
description: ExperienceLevel represents the player's experience level (0=beginner,
1=easy, 2=medium, 3=hard, 4=very hard, 5=impossible)
info:
description: Info contains additional information about the player.
type: string
location:
description: Location indicates the player's real-life location.
type: string
name:
description: Name is the name of the player.
type: string
type: object
account.CreateAccountArgs:
description: Structure for the account creation request payload.
properties:
Expand Down Expand Up @@ -57,23 +78,15 @@ definitions:
account.UpdateAccountArgs:
description: Structure for the account update request payload.
properties:
account:
allOf:
- $ref: '#/definitions/account.AccountParam'
description: The account to create.
account_id:
description: The account ID for the account that will be updated.
type: integer
email:
description: The new email for the account.
type: string
experience_level:
description: The new experience level for the account.
type: integer
info:
description: The new info for the account.
type: string
location:
description: The new location for the account.
type: string
name:
description: The new name for the account.
password:
description: The password for the account to be created
type: string
type: object
game.CreateGameArgs:
Expand Down
20 changes: 20 additions & 0 deletions internal/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,23 @@ type Account struct {
// ExperienceLevel represents the player's experience level (0=beginner, 1=easy, 2=medium, 3=hard, 4=very hard, 5=impossible)
ExperienceLevel ExperienceLevel `json:"experience_level"`
}

// AccountParam represents a player account with non-required fields.
//
// @Description Structure for representing a player account with non-required fields.
type AccountParam struct {
// Name is the name of the player.
Name *string `json:"name,omitempty"`

// Info contains additional information about the player.
Info *string `json:"info,omitempty"`

// Location indicates the player's real-life location.
Location *string `json:"location,omitempty"`

// Email is the email address of the player.
Email *string `json:"email,omitempty"`

// ExperienceLevel represents the player's experience level (0=beginner, 1=easy, 2=medium, 3=hard, 4=very hard, 5=impossible)
ExperienceLevel *ExperienceLevel `json:"experience_level,omitempty"`
}
9 changes: 5 additions & 4 deletions internal/account/create_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import (
"bytes"
"crypto/rand"
"database/sql"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"net/mail"

"encoding/hex"

argon2 "golang.org/x/crypto/argon2"
)

Expand Down Expand Up @@ -60,6 +59,8 @@ func isEmailValid(email string, db *sql.DB) (bool, error) {
return true, nil
}

var Hasher = NewArgon2idHash(1, 32, 64*1024, 32, 256)

// CreateAccount handles the creation of a new account.
//
// @Summary Create a new account
Expand Down Expand Up @@ -120,7 +121,7 @@ func CreateAccount(w http.ResponseWriter, r *http.Request, db *sql.DB) error {
return errors.New("the provided email is not valid")
}

hashSalt, err := NewArgon2idHash(1, 32, 64*1024, 32, 256).GenerateHash([]byte(account.Password), nil)
hashSalt, err := Hasher.GenerateHash([]byte(account.Password), nil)
if err != nil {
log.Println("error saving a password: ", err.Error())
return errors.New("an error occurred while saving the password. Please try again later")
Expand Down Expand Up @@ -229,7 +230,7 @@ func (a *Argon2idHash) Compare(hash, salt, password []byte) error {
}

func storeHashAndSalt(hashSalt *HashSalt, accountEmail string, db *sql.DB) error {
result, err := db.Query("INSERT INTO passwords (account_email, hash, salt) VALUES ($1, $2, $3)", accountEmail, hex.EncodeToString(hashSalt.hash), hex.EncodeToString(hashSalt.salt))
result, err := db.Query("INSERT INTO passwords (account_email, hash, salt) VALUES ($1, $2, $3)", accountEmail, base64.StdEncoding.EncodeToString(hashSalt.hash), base64.StdEncoding.EncodeToString(hashSalt.salt))
if err != nil {
return errors.New("an error occurred while inserting a hash-salt pair into the database: " + err.Error())
}
Expand Down
Loading

0 comments on commit 46d96ac

Please sign in to comment.