Skip to content

Commit

Permalink
Merge pull request #19 from justinfarrelldev/account-deletion
Browse files Browse the repository at this point in the history
Created a deletion endpoint for accounts
  • Loading branch information
justinfarrelldev authored Dec 9, 2024
2 parents 5744db2 + 4a3ea6f commit 1683229
Show file tree
Hide file tree
Showing 6 changed files with 668 additions and 2 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ You absolutely can, so long as you follow the license in `LICENSE.md`. I am real
- [ ] Passwords can be reset
- [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]
- [ ] Account updates require proof of ownership
- [x] Account updates require proof of ownership
- [ ] All account endpoints are rate-limited appropriately

### Lobbies (/lobby)
Expand All @@ -199,7 +199,7 @@ You absolutely can, so long as you follow the license in `LICENSE.md`. I am real
- [ ] "Player connected" event is sent when players join the lobby
- [ ] Chats can be sent in lobbies
- [ ] All lobby endpoints are rate-limited appropriately
- [ ] Lobby updates require proof of ownership
- [x] Lobby updates require proof of ownership

### Games (/game)
*Note: profiles can be changed in the game (as seen in the UI), but this should be handled client-side using the account endpoints.
Expand Down
60 changes: 60 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,52 @@ const docTemplate = `{
}
}
},
"/account/delete_account": {
"delete": {
"description": "This endpoint deletes a player account.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"account"
],
"summary": "Deletes an account",
"parameters": [
{
"description": "account deletion request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/account.DeleteAccountArgs"
}
}
],
"responses": {
"200": {
"description": "Successfully deleted account!",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {}
},
"403": {
"description": "Forbidden",
"schema": {}
},
"500": {
"description": "Internal Server Error",
"schema": {}
}
}
}
},
"/account/get_account": {
"get": {
"description": "This endpoint gets a multiplayer account's info.",
Expand Down Expand Up @@ -599,6 +645,20 @@ const docTemplate = `{
}
}
},
"account.DeleteAccountArgs": {
"description": "Structure for the account deletion request payload.",
"type": "object",
"properties": {
"account_id": {
"description": "The account ID for the account that will be deleted.",
"type": "integer"
},
"session_id": {
"description": "A valid session ID for the account (so we know they are signed in)",
"type": "integer"
}
}
},
"account.ExperienceLevel": {
"type": "integer",
"enum": [
Expand Down
60 changes: 60 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,52 @@
}
}
},
"/account/delete_account": {
"delete": {
"description": "This endpoint deletes a player account.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"account"
],
"summary": "Deletes an account",
"parameters": [
{
"description": "account deletion request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/account.DeleteAccountArgs"
}
}
],
"responses": {
"200": {
"description": "Successfully deleted account!",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {}
},
"403": {
"description": "Forbidden",
"schema": {}
},
"500": {
"description": "Internal Server Error",
"schema": {}
}
}
}
},
"/account/get_account": {
"get": {
"description": "This endpoint gets a multiplayer account's info.",
Expand Down Expand Up @@ -590,6 +636,20 @@
}
}
},
"account.DeleteAccountArgs": {
"description": "Structure for the account deletion request payload.",
"type": "object",
"properties": {
"account_id": {
"description": "The account ID for the account that will be deleted.",
"type": "integer"
},
"session_id": {
"description": "A valid session ID for the account (so we know they are signed in)",
"type": "integer"
}
}
},
"account.ExperienceLevel": {
"type": "integer",
"enum": [
Expand Down
42 changes: 42 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ definitions:
description: The password for the account to be created
type: string
type: object
account.DeleteAccountArgs:
description: Structure for the account deletion request payload.
properties:
account_id:
description: The account ID for the account that will be deleted.
type: integer
session_id:
description: A valid session ID for the account (so we know they are signed
in)
type: integer
type: object
account.ExperienceLevel:
enum:
- 0
Expand Down Expand Up @@ -246,6 +257,37 @@ paths:
summary: Create a new account
tags:
- account
/account/delete_account:
delete:
consumes:
- application/json
description: This endpoint deletes a player account.
parameters:
- description: account deletion request body
in: body
name: body
required: true
schema:
$ref: '#/definitions/account.DeleteAccountArgs'
produces:
- application/json
responses:
"200":
description: Successfully deleted account!
schema:
type: string
"400":
description: Bad Request
schema: {}
"403":
description: Forbidden
schema: {}
"500":
description: Internal Server Error
schema: {}
summary: Deletes an account
tags:
- account
/account/get_account:
get:
consumes:
Expand Down
99 changes: 99 additions & 0 deletions internal/account/delete_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package account

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"

"github.com/jmoiron/sqlx"
"github.com/justinfarrelldev/open-ctp-server/internal/auth"
)

// DeleteAccountArgs represents the expected structure of the request body for deleting an account.
//
// @Description Structure for the account deletion request payload.
type DeleteAccountArgs struct {
// The account ID for the account that will be deleted.
AccountId int64 `json:"account_id"`
// A valid session ID for the account (so we know they are signed in)
SessionId *int64 `json:"session_id,omitempty"`
}

// DeleteAccount deletes an account by the account ID.
//
// @Summary Deletes an account
// @Description This endpoint deletes a player account.
// @Tags account
// @Accept json
// @Produce json
// @Param body body DeleteAccountArgs true "account deletion request body"
// @Success 200 {string} string "Successfully deleted account!"
// @Failure 400 {object} error "Bad Request"
// @Failure 403 {object} error "Forbidden"
// @Failure 500 {object} error "Internal Server Error"
// @Router /account/delete_account [delete]
func DeleteAccount(w http.ResponseWriter, r *http.Request, db *sqlx.DB, store *auth.SessionStore) error {

if r.Method != http.MethodDelete {
return errors.New("invalid request; request must be a DELETE request")
}

decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()

args := DeleteAccountArgs{}
err := decoder.Decode(&args)

if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return errors.New("an error occurred while decoding the request body: " + err.Error())
}

if args.AccountId == 0 {
w.WriteHeader(http.StatusBadRequest)
return errors.New("account_id must be specified")
}

if args.SessionId == nil {
w.WriteHeader(http.StatusBadRequest)
return errors.New("a valid session_id must be specified")
}

session, err := store.GetSession(strconv.FormatInt(*args.SessionId, 10))

if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return errors.New("an error occurred while retrieving the session: " + err.Error())
}

if session == nil {
w.WriteHeader(http.StatusInternalServerError)
return errors.New("session not found")
}

if session.IsExpired() {
w.WriteHeader(http.StatusForbidden)
return errors.New("session has expired")
}

query := "DELETE FROM account WHERE id = $1"
result, err := db.Exec(query, args.AccountId)
if err != nil {
return fmt.Errorf("an error occurred while deleting the account with the ID %d: %v", args.AccountId, err)
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("an error occurred while checking the affected rows: %v", err)
}

if rowsAffected == 0 {
return fmt.Errorf("no account exists with the ID %d", args.AccountId)
}

w.WriteHeader(http.StatusOK)
w.Write([]byte("Successfully deleted account!"))
return nil
}
Loading

0 comments on commit 1683229

Please sign in to comment.