Skip to content

Commit

Permalink
User struct + CRUD operations (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
sneha-afk authored Feb 10, 2024
1 parent c839348 commit 2a4ef92
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 31 deletions.
75 changes: 67 additions & 8 deletions authentication/user_accounts.go → authentication/auth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ import (

"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"slugquest.com/backend/crud"
)

const FRONTEND_HOST string = "localhost:5185"

// TODO: make this more elegant with Gin sessions or something
var Curr_user_id string = "hi"

// Checks if user is authenticated before redirecting to next page
func IsAuthenticated(c *gin.Context) {
// Auth token: for direct calls to this endpoint
Expand Down Expand Up @@ -46,6 +44,8 @@ func LoginHandler(auth *Authenticator) gin.HandlerFunc {

// Save the state inside the session.
session := sessions.Default(c)
session.Clear()

session.Set("state", state)
if err := session.Save(); err != nil {
c.String(http.StatusInternalServerError, err.Error())
Expand Down Expand Up @@ -130,20 +130,79 @@ func CallbackHandler(auth *Authenticator) gin.HandlerFunc {
return
}

// Extract Auth0's provided user vid
if profile["sub"] == nil {
var userInfoStruct *crud.User = getUserInfo(c)
if userInfoStruct == nil {
c.String(http.StatusInternalServerError, "Couldn't retrieve user profile.")
return
}

session.Set("user_profile", userInfoStruct)
if err := session.Save(); err != nil {
c.String(http.StatusInternalServerError, err.Error())
return
}
user_id := profile["sub"].(string)[len("auth0|"):]
Curr_user_id = user_id

// Redirect to logged in page.
c.Redirect(http.StatusTemporaryRedirect, "http://"+FRONTEND_HOST+"/loggedin")
}
}

// Displays user profile from the current session
func getUserInfo(c *gin.Context) *crud.User {
session := sessions.Default(c)

profile, ok := session.Get("profile").(map[string]interface{})
if !ok || profile == nil {
c.String(http.StatusInternalServerError, "Couldn't retrieve user profile.")
return nil
}

// No user id? No SlugQuest.
sesUID, ok := profile["sub"].(string)
if !ok {
c.String(http.StatusInternalServerError, "Couldn't resolve user id.")
return nil
}

sesUsername, ok := profile["name"].(string)
if !ok {
c.String(http.StatusInternalServerError, "Couldn't resolve username.")
return nil
}

sesPFP, ok := profile["picture"].(string)
if !ok {
c.String(http.StatusInternalServerError, "Couldn't resolve profile picture URL.")
return nil
}

// Check if user exists in our DB
user, found, err := crud.GetUser(sesUID)
if err != nil {
c.String(http.StatusInternalServerError, "Couldn't fetch user.")
return nil
}

if !found {
// Need to populate and add a new user
user = crud.User{
UserID: sesUID,
Username: sesUsername,
Picture: sesPFP,
Points: 0,
BossId: 0,
}

added, err := crud.AddUser(user)
if err != nil || !added {
c.String(http.StatusInternalServerError, "Couldn't register user into our records.")
return nil
}
}

return &user
}

// Sends user profile from the current session as JSON
// func UserProfileHandler(c *gin.Context) {
// session := sessions.Default(c)
// profile := session.Get("profile")
Expand Down
4 changes: 2 additions & 2 deletions authentication/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ type Authenticator struct {
oauth2.Config
}

// New instantiates the *Authenticator.
func New() (*Authenticator, error) {
// NewAuthenticator instantiates the *Authenticator.
func NewAuthenticator() (*Authenticator, error) {
provider, err := oidc.NewProvider(
context.Background(),
"https://"+os.Getenv("AUTH0_DOMAIN")+"/",
Expand Down
108 changes: 108 additions & 0 deletions crud/db_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ type TaskPreview struct {
IsAllDay bool
}

type User struct {
UserID string // Not known to user, do not expose
Username string // Set by user, can be exposed
Picture string // A0 stores their profile pics as URLs
Points int
BossId int
}

var DB *sqlx.DB

func LoadDumbData() error {
Expand Down Expand Up @@ -290,3 +298,103 @@ func GetTaskId(Tid int) (Task, bool, error) {
print(counter)
return taskit, counter == 1, err
}

// Find user by UserID
func GetUser(uid string) (User, bool, error) {
rows, err := DB.Query("SELECT * FROM UserTable WHERE UserID=?;", uid)
var user User
if err != nil {
fmt.Println(err)
return user, false, err
}

counter := 0
for rows.Next() {
counter += 1
rows.Scan(&user.UserID, &user.Points, &user.BossId)
}
rows.Close()

return user, counter == 1, err
}

// Add user into DB
func AddUser(u User) (bool, error) {
tx, err := DB.Beginx()
if err != nil {
fmt.Printf("AddUser(): breaky 1: %v", err)
return false, err
}
defer tx.Rollback() // aborrt transaction if error

stmt, err := tx.Preparex("INSERT INTO UserTable (UserID, Points, Bossid) VALUES (?, ?, ?)")
if err != nil {
fmt.Printf("AddUser(): breaky 2: %v", err)
return false, err
}

defer stmt.Close() //defer the closing of SQL statement to ensure it Closes once the function completes
_, err = stmt.Exec(u.UserID, u.Points, u.BossId)
if err != nil {
fmt.Printf("AddUser(): breaky 3: %v", err)
return false, err
}

tx.Commit() //commit transaction to database

return true, nil
}

// Edit a user by supplying new values
func EditUser(u User, uid string) (bool, error) {
tx, err := DB.Beginx()
if err != nil {
return false, err
}
defer tx.Rollback() // aborrt transaction if error

stmt, err := tx.Preparex(`
UPDATE UserTable
SET UserID = ?, Points = ?, Bossid = ?
WHERE UserID = ?
`)
if err != nil {
return false, err
}

defer stmt.Close()

_, err = stmt.Exec(u.UserID, u.Points, u.BossId, uid)
if err != nil {
return false, err
}

tx.Commit()

return true, nil
}

func DeleteUser(uid string) (bool, error) {
tx, err := DB.Beginx()
if err != nil {
return false, err
}
defer tx.Rollback() // aborrt transaction if error

stmt, err := tx.Preparex("DELETE FROM UserTable WHERE UserID = ?")
if err != nil {
fmt.Println("DeleteUser: breaky 1")
return false, err
}
defer stmt.Close()

_, err = stmt.Exec(uid)
if err != nil {
fmt.Println("DeleteUser: breaky 2")
return false, err
}

tx.Commit()

return true, nil
}
8 changes: 5 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"fmt"
"log"

envfuncs "github.com/joho/godotenv"
Expand All @@ -16,12 +15,14 @@ func main() {
// Load .env
if env_err := envfuncs.Load(); env_err != nil {
log.Fatalf("Error loading the .env file: %v", env_err)
return
}

// Create new authenticator to pass to the router
auth, auth_err := authentication.New()
auth, auth_err := authentication.NewAuthenticator()
if auth_err != nil {
log.Fatalf("Failed to initialize the authenticator: %v", auth_err)
return
}
router := CreateRouter(auth)

Expand All @@ -34,10 +35,11 @@ func main() {
dummy_err := crud.LoadDumbData()
if dummy_err != nil {
log.Fatalf("error loaduing dumb data: %v", dummy_err)
return
}
utest := testing.RunAllTests()
if !utest {
fmt.Println("unit test failure")
log.Fatal("unit test failure")
return
}

Expand Down
19 changes: 9 additions & 10 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func CreateRouter(auth *authentication.Authenticator) *gin.Engine {
// To store custom types in our cookies,
// we must first register them using gob.Register
gob.Register(map[string]interface{}{})
gob.Register(crud.User{})

// Set up cookie store for the user session
store := cookie.NewStore([]byte("secret"))
Expand Down Expand Up @@ -134,16 +135,14 @@ func deleteTask(c *gin.Context) {

// Returns a list of all tasks of the current user
func getAllUserTasks(c *gin.Context) {
// TODO: ill be fixing this
// user_id stored as a variable within the session
// uid := c.GetString("user_id")
// log.Printf("found userid = %v", uid)
// if uid == "" {
// log.Println("getAllUserTasks(): couldn't get user_id")
// c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retreive user id"})
// return
// }
uid := authentication.Curr_user_id
// Retrieve the user_id through the struct stored in the session
session := sessions.Default(c)
userProfile, ok := session.Get("user_profile").(crud.User)
if !ok {
c.String(http.StatusInternalServerError, "Couldn't retreive user's id to display tasks.")
return
}
uid := userProfile.UserID

arr, err := crud.GetUserTask(uid)
if err != nil {
Expand Down
7 changes: 5 additions & 2 deletions schema.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
CREATE TABLE UserTable (
UserID VARCHAR(255) PRIMARY KEY NOT NULL-- Assuming Auth0 provides a string-based user ID
CREATE TABLE IF NOT EXISTS UserTable (
UserID VARCHAR(255) PRIMARY KEY NOT NULL,
Points INTEGER NOT NULL,
BossId INTEGER NOT NULL,
FOREIGN KEY (BossId) REFERENCES BossTable(BossID)
);

CREATE TABLE TaskTable (
Expand Down
Loading

0 comments on commit 2a4ef92

Please sign in to comment.