Skip to content

Commit

Permalink
feat: added fetch profile route
Browse files Browse the repository at this point in the history
  • Loading branch information
harshkhandeparkar committed Dec 16, 2023
1 parent 608136a commit 3f93fd4
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 4 deletions.
4 changes: 2 additions & 2 deletions controllers/mentor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,10 @@ func TestMentorDashboardOK(t *testing.T) {
var resMentor controllers.MentorDashboard
_ = json.NewDecoder(res.Body).Decode(&resMentor)

fmt.Printf("%+v %+v", testMentor, resMentor)

expectStatusCodeToBe(t, res, http.StatusOK)
if !reflect.DeepEqual(testMentor, resMentor) {
t.Fatalf("Incorrect data returned from /mentor/dashboard/")
fmt.Printf("Expected mentor dashboard: %#v\n\n", testMentor)
fmt.Printf("Received mentor dashboard: %#v\n", resMentor)
}
}
86 changes: 86 additions & 0 deletions controllers/profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package controllers

import (
"net/http"

"github.com/kossiitkgp/kwoc-backend/v2/middleware"
"github.com/kossiitkgp/kwoc-backend/v2/utils"
"gorm.io/gorm"

"github.com/kossiitkgp/kwoc-backend/v2/models"
)

type ProfileResBodyFields struct {
Username string `json:"username"`
Name string `json:"name"`
Email string `json:"email"`
// `mentor` or `student`
Type string `json:"type"`
}

// FetchProfile godoc
// @Summary Fetches user profile
// @Description Fetches the user's profile from the JWT, if it is valid. If invalid, returns an error.
// @Accept plain
// @Produce json
// @Success 200 {object} ProfileResBodyFields "Succesfully authenticated."
// @Failure 400 {object} utils.HTTPMessage "User is not registered."
// @Failure 401 {object} utils.HTTPMessage "JWT session token invalid."
// @Failure 500 {object} utils.HTTPMessage "Error parsing JWT string."
//
// @Security JWT
//
// @Router /profile [get]
func FetchProfile(w http.ResponseWriter, r *http.Request) {
app := r.Context().Value(middleware.APP_CTX_KEY).(*middleware.App)
db := app.Db

username := r.Context().Value(middleware.LOGIN_CTX_USERNAME_KEY).(string)

// Check if the student already exists in the db
student := models.Student{}
tx := db.
Table("students").
Where("username = ?", username).
First(&student)

if tx.Error != nil && tx.Error != gorm.ErrRecordNotFound {
utils.LogErrAndRespond(r, w, tx.Error, "Database error.", http.StatusInternalServerError)
return
}

student_exists := student.Username == username
if student_exists {
utils.RespondWithJson(r, w, ProfileResBodyFields{
Username: student.Username,
Name: student.Name,
Email: student.Email,
Type: "student",
})
return
}

// Check if a mentor of the same username exists
mentor := models.Mentor{}
tx = db.
Table("mentors").
Where("username = ?", username).
First(&mentor)
if tx.Error != nil && tx.Error != gorm.ErrRecordNotFound {
utils.LogErrAndRespond(r, w, tx.Error, "Database error.", http.StatusInternalServerError)
return
}
mentor_exists := mentor.Username == username

if mentor_exists {
utils.RespondWithJson(r, w, ProfileResBodyFields{
Username: mentor.Username,
Name: mentor.Name,
Email: mentor.Email,
Type: "mentor",
})
return
}

utils.RespondWithHTTPMessage(r, w, http.StatusBadRequest, "User is not registered.")
}
2 changes: 1 addition & 1 deletion controllers/student.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func RegisterStudent(w http.ResponseWriter, r *http.Request) {
First(&student)

if tx.Error != nil && tx.Error != gorm.ErrRecordNotFound {
utils.LogErrAndRespond(r, w, err, "Database error.", http.StatusInternalServerError)
utils.LogErrAndRespond(r, w, tx.Error, "Database error.", http.StatusInternalServerError)
return
}

Expand Down
5 changes: 5 additions & 0 deletions middleware/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ func WithLogin(inner http.HandlerFunc) http.HandlerFunc {
return
}

if err == utils.ErrJwtTokenExpired {
utils.LogErrAndRespond(r, w, err, "Error: JWT session token expired.", http.StatusUnauthorized)
return
}

utils.LogErrAndRespond(r, w, err, "Error parsing JWT string.", http.StatusInternalServerError)
return
}
Expand Down
6 changes: 6 additions & 0 deletions server/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ func getRoutes(app *middleware.App) []Route {
"/oauth/",
middleware.WrapApp(app, controllers.OAuth),
},
{
"Profile",
"GET",
"/profile/",
middleware.WithLogin(middleware.WrapApp(app, controllers.FetchProfile)),
},
{
"Fetch Student Details",
"GET",
Expand Down
8 changes: 7 additions & 1 deletion utils/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"errors"
"fmt"
"os"
"strconv"
"time"
Expand All @@ -11,6 +12,7 @@ import (
)

var ErrJwtSecretKeyNotFound = errors.New("ERROR: JWT SECRET KEY NOT FOUND")
var ErrJwtTokenExpired = errors.New("ERROR: JWT TOKEN EXPIRED")
var ErrJwtTokenInvalid = errors.New("ERROR: JWT TOKEN INVALID")

func getJwtKey() (string, error) {
Expand Down Expand Up @@ -46,6 +48,10 @@ func ParseLoginJwtString(tokenString string) (*jwt.Token, *LoginJwtClaims, error
var loginClaims = LoginJwtClaims{}
token, err := jwt.ParseWithClaims(tokenString, &loginClaims, jwtKeyFunc)

if err.Error() == fmt.Sprintf("%s: %s", jwt.ErrTokenInvalidClaims.Error(), jwt.ErrTokenExpired.Error()) {
return nil, nil, ErrJwtTokenExpired
}

if err != nil {
return nil, nil, err
}
Expand All @@ -66,7 +72,7 @@ func GenerateLoginJwtString(loginJwtFields LoginJwtFields) (string, error) {

if err != nil {
// Default of 30 days
jwtValidityTime = 30 * 24
jwtValidityTime = 0

log.Warn().Msgf("Could not parse JWT validity time from the environment. Set to default of %d hours.", jwtValidityTime)
}
Expand Down

0 comments on commit 3f93fd4

Please sign in to comment.