diff --git a/cmd/api/main.go b/cmd/api/main.go index c3bbaf1..1260196 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -8,6 +8,7 @@ import ( logger "github.com/CodeChefVIT/cookoff-backend/internal/helpers/logging" "github.com/CodeChefVIT/cookoff-backend/internal/helpers/queue" "github.com/CodeChefVIT/cookoff-backend/internal/helpers/submission" + "github.com/CodeChefVIT/cookoff-backend/internal/helpers/validator" "github.com/CodeChefVIT/cookoff-backend/internal/server" "github.com/CodeChefVIT/cookoff-backend/internal/worker" "github.com/hibiken/asynq" @@ -20,6 +21,7 @@ func main() { database.InitCache() auth.InitJWT() submission.Init(database.RedisClient) + validator.InitValidator() taskServer, taskClient := queue.InitQueue("redis:6379", 2) diff --git a/go.mod b/go.mod index 0ba15a7..ec057ca 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22 require ( github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/jwtauth/v5 v5.3.1 + github.com/go-playground/validator v9.31.0+incompatible github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.6.0 @@ -14,11 +15,15 @@ require ( ) require ( + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/spf13/cast v1.3.1 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect google.golang.org/protobuf v1.26.0 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect ) require ( @@ -38,7 +43,7 @@ require ( github.com/lestrrat-go/option v1.0.1 // indirect github.com/segmentio/asm v1.2.0 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.21.0 golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index 1c65bb9..b0d6c94 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,12 @@ github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/jwtauth/v5 v5.3.1 h1:1ePWrjVctvp1tyBq5b/2ER8Th/+RbYc7x4qNsc5rh5A= github.com/go-chi/jwtauth/v5 v5.3.1/go.mod h1:6Fl2RRmWXs3tJYE1IQGX81FsPoGqDwq9c15j52R5q80= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA= +github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= @@ -45,6 +51,8 @@ github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= @@ -128,6 +136,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/controllers/auth.go b/internal/controllers/auth.go index 67f5d41..792711b 100644 --- a/internal/controllers/auth.go +++ b/internal/controllers/auth.go @@ -11,21 +11,22 @@ import ( "github.com/CodeChefVIT/cookoff-backend/internal/helpers/database" httphelpers "github.com/CodeChefVIT/cookoff-backend/internal/helpers/http" logger "github.com/CodeChefVIT/cookoff-backend/internal/helpers/logging" + "github.com/CodeChefVIT/cookoff-backend/internal/helpers/validator" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "golang.org/x/crypto/bcrypt" ) type LoginRequest struct { - Email string `json:"email"` - Password string `json:"password"` + Email string `json:"email" validate:"required"` + Password string `json:"password" validate:"required"` } type SignupRequest struct { - Email string `json:"email"` - Name string `json:"name"` - RegNo string `json:"reg_no"` - Key string `json:"fuck_you"` + Email string `json:"email" validate:"required"` + Name string `json:"name" validate:"required"` + RegNo string `json:"reg_no" validate:"required"` + Key string `json:"fuck_you" validate:"required"` } func SignUp(w http.ResponseWriter, r *http.Request) { @@ -36,6 +37,11 @@ func SignUp(w http.ResponseWriter, r *http.Request) { return } + if err := validator.ValidatePayload(w, payload); err != nil { + httphelpers.WriteError(w, http.StatusNotAcceptable, "Please provide values for all required fields.") + return + } + if payload.Key != os.Getenv("SECRET_KEY_FUCKERS") { httphelpers.WriteError(w, http.StatusUnauthorized, "I WILL POP YOUR CHERRY BRO") return diff --git a/internal/controllers/questions.go b/internal/controllers/questions.go index 5a70bac..e0820a8 100644 --- a/internal/controllers/questions.go +++ b/internal/controllers/questions.go @@ -8,6 +8,7 @@ import ( "github.com/CodeChefVIT/cookoff-backend/internal/helpers/auth" "github.com/CodeChefVIT/cookoff-backend/internal/helpers/database" httphelpers "github.com/CodeChefVIT/cookoff-backend/internal/helpers/http" + "github.com/CodeChefVIT/cookoff-backend/internal/helpers/validator" "github.com/go-chi/chi/v5" "github.com/google/uuid" "github.com/jackc/pgx/v5" @@ -25,6 +26,17 @@ type Question struct { OutputFormat *string `json:"output_format"` } +type QuestionRequest struct { + ID uuid.UUID `json:"id" validate:"required"` + Description *string `json:"description"` + Title *string `json:"title"` + InputFormat *string `json:"input_format"` + Points pgtype.Int4 `json:"points"` + Round int32 `json:"round"` + Constraints *string `json:"constraints"` + OutputFormat *string `json:"output_format"` +} + func GetAllQuestion(w http.ResponseWriter, r *http.Request) { ctx := r.Context() fetchedQuestions, err := database.Queries.GetQuestions(ctx) @@ -79,6 +91,11 @@ func CreateQuestion(w http.ResponseWriter, r *http.Request) { return } + if err := validator.ValidatePayload(w, question); err != nil { + httphelpers.WriteError(w, http.StatusNotAcceptable, "Please provide values for all required fields.") + return + } + questions, err := database.Queries.CreateQuestion(ctx, db.CreateQuestionParams{ ID: uuid.New(), Description: question.Description, @@ -116,12 +133,17 @@ func DeleteQuestion(w http.ResponseWriter, r *http.Request) { func UpdateQuestion(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - var updateQuestion Question + var updateQuestion QuestionRequest if err := httphelpers.ParseJSON(r, &updateQuestion); err != nil { httphelpers.WriteError(w, http.StatusInternalServerError, err.Error()) return } + if err := validator.ValidatePayload(w, updateQuestion); err != nil { + httphelpers.WriteError(w, http.StatusNotAcceptable, "Please provide values for all required fields.") + return + } + question, err := database.Queries.GetQuestion(ctx, updateQuestion.ID) if err != nil { if err == pgx.ErrNoRows { diff --git a/internal/controllers/runcode.go b/internal/controllers/runcode.go index 97b97a0..163ae58 100644 --- a/internal/controllers/runcode.go +++ b/internal/controllers/runcode.go @@ -12,6 +12,7 @@ import ( httphelpers "github.com/CodeChefVIT/cookoff-backend/internal/helpers/http" logger "github.com/CodeChefVIT/cookoff-backend/internal/helpers/logging" "github.com/CodeChefVIT/cookoff-backend/internal/helpers/submission" + "github.com/CodeChefVIT/cookoff-backend/internal/helpers/validator" "github.com/google/uuid" ) @@ -29,6 +30,11 @@ func RunCode(w http.ResponseWriter, r *http.Request) { return } + if err := validator.ValidatePayload(w, req); err != nil { + httphelpers.WriteError(w, http.StatusNotAcceptable, "Please provide values for all required fields.") + return + } + question_id, _ := uuid.Parse(req.QuestionID) userID, _ := auth.GetUserID(w, r) diff --git a/internal/controllers/submission.go b/internal/controllers/submission.go index f94496f..c525539 100644 --- a/internal/controllers/submission.go +++ b/internal/controllers/submission.go @@ -13,13 +13,14 @@ import ( httphelpers "github.com/CodeChefVIT/cookoff-backend/internal/helpers/http" logger "github.com/CodeChefVIT/cookoff-backend/internal/helpers/logging" "github.com/CodeChefVIT/cookoff-backend/internal/helpers/submission" + "github.com/CodeChefVIT/cookoff-backend/internal/helpers/validator" "github.com/google/uuid" ) type subreq struct { - SourceCode string `json:"source_code"` - LanguageID int `json:"language_id"` - QuestionID string `json:"question_id"` + SourceCode string `json:"source_code" validate:"required"` + LanguageID int `json:"language_id" validate:"required"` + QuestionID string `json:"question_id" validate:"required"` } var JUDGE0_URI = os.Getenv("JUDGE0_URI") @@ -34,6 +35,11 @@ func SubmitCode(w http.ResponseWriter, r *http.Request) { return } + if err := validator.ValidatePayload(w, req); err != nil { + httphelpers.WriteError(w, http.StatusNotAcceptable, "Please provide values for all required fields.") + return + } + userID, _ := auth.GetUserID(w, r) nullUserID := uuid.NullUUID{UUID: userID, Valid: true} diff --git a/internal/controllers/verify_role.go b/internal/helpers/auth/verify_role.go similarity index 97% rename from internal/controllers/verify_role.go rename to internal/helpers/auth/verify_role.go index 672523b..8ee1184 100644 --- a/internal/controllers/verify_role.go +++ b/internal/helpers/auth/verify_role.go @@ -1,4 +1,4 @@ -package controllers +package auth import ( "fmt" diff --git a/internal/helpers/validator/validator.go b/internal/helpers/validator/validator.go new file mode 100644 index 0000000..f664e25 --- /dev/null +++ b/internal/helpers/validator/validator.go @@ -0,0 +1,25 @@ +package validator + +import ( + "net/http" + + httphelpers "github.com/CodeChefVIT/cookoff-backend/internal/helpers/http" + logger "github.com/CodeChefVIT/cookoff-backend/internal/helpers/logging" + "github.com/go-playground/validator" +) + +var Validate *validator.Validate + +func InitValidator() { + Validate = validator.New() +} + +func ValidatePayload(w http.ResponseWriter, v any) error { + if err := Validate.Struct(v); err != nil { + logger.Errof("Please provide values for all required fields.") + httphelpers.WriteError(w, http.StatusNotAcceptable, err.Error()) + return err + } + httphelpers.WriteJSON(w, http.StatusOK, map[string]string{"message": "Payload Validated"}) + return nil +} \ No newline at end of file diff --git a/internal/middlewares/role_authorization.go b/internal/middlewares/role_authorization.go index 5c72daf..22fcd61 100644 --- a/internal/middlewares/role_authorization.go +++ b/internal/middlewares/role_authorization.go @@ -3,14 +3,16 @@ package middlewares import ( "net/http" - "github.com/CodeChefVIT/cookoff-backend/internal/controllers" + "github.com/CodeChefVIT/cookoff-backend/internal/helpers/auth" + httphelpers "github.com/CodeChefVIT/cookoff-backend/internal/helpers/http" ) func RoleAuthorizationMiddleware(requiredRole string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ok := controllers.RoleFromToken(w, r, requiredRole) + ok := auth.RoleFromToken(w, r, requiredRole) if !ok { + httphelpers.WriteError(w, http.StatusUnauthorized, "Not Authorized") return } next.ServeHTTP(w, r)