Commit 62deb676 authored by Julien Schröter's avatar Julien Schröter

Add validation whether user can answer question on /answer endpoint

parent 3ebb685b
Pipeline #1844 passed with stages
in 19 minutes and 42 seconds
......@@ -232,6 +232,7 @@ func getDuelRound(repository DuelQuery) gin.HandlerFunc {
if errBind := ctx.BindQuery(&params); errBind != nil {
ctx.AbortWithError(http.StatusBadRequest, errBind)
return
}
userID, err := endpoint.GetUserID(ctx)
......@@ -270,13 +271,16 @@ func postAnswer(repository DuelQuery, titleRepository title.TitleQuery, sessionR
if errBindParams := ctx.BindQuery(&reqParams); errBindParams != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, errBindParams.Error())
return
}
if errBindBody := ctx.BindJSON(&reqBody); errBindBody != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, errBindBody.Error())
return
}
answerType, errAnswerType := reqBody.AnswerType()
if errAnswerType != nil {
ctx.AbortWithError(http.StatusBadRequest, errAnswerType)
return
}
userID, err := endpoint.GetUserID(ctx)
......@@ -287,8 +291,19 @@ func postAnswer(repository DuelQuery, titleRepository title.TitleQuery, sessionR
if isplayer, err := repository.IsPlayerOfRound(userID, reqParams.RoundID); err != nil {
raven.CaptureError(err, nil)
ctx.AbortWithError(http.StatusInternalServerError, err)
return
} else if !isplayer {
ctx.AbortWithStatus(http.StatusForbidden)
return
}
if permitted, err := repository.CanAnswerQuestion(userID, reqParams.RoundID, reqParams.QuestionID); err != nil {
raven.CaptureError(err, nil)
ctx.AbortWithError(http.StatusInternalServerError, err)
return
} else if !permitted {
ctx.AbortWithStatus(http.StatusForbidden)
return
}
// insert the useranswer
......@@ -323,6 +338,7 @@ func postAnswer(repository DuelQuery, titleRepository title.TitleQuery, sessionR
if errNewTitles != nil {
raven.CaptureError(errNewTitles, nil)
ctx.AbortWithError(http.StatusInternalServerError, errNewTitles)
return
}
awards.Titles = newTitles
......
......@@ -273,6 +273,7 @@ type DuelQuery interface {
InsertOptionUserAnswer(roundID, questionID, userID uint32, answer []uint32) (*UserAwards, error)
InsertTextUserAnswer(roundID, questionID, userID uint32, answer string) (*UserAwards, error)
IsPlayerOfRound(userID, roundID uint32) (bool, error)
CanAnswerQuestion(userID, roundID, questionID uint32) (bool, error)
}
// MySQLDuelQuery provides functionality to create, get and update duels in a MySQL database.
......@@ -1449,3 +1450,19 @@ func (m *MySQLDuelQuery) IsPlayerOfRound(userID, roundID uint32) (bool, error) {
}
return exists, nil
}
func (*MySQLDuelQuery) CanAnswerQuestion(userID, roundID, questionID uint32) (bool, error) {
db, err := dbhandler.GetDBConnection()
if err != nil {
return false, fmt.Errorf("failed to get database connection instance: %v", err)
}
row := db.QueryRow("SELECT COUNT(`iduseranswer`) FROM `useranswer` JOIN `roundquestion` ON (`useranswer`.`roundquestion`=`roundquestion`.`idroundquestion`) WHERE `useranswer`.`user`=? AND `roundquestion`.`round`=? AND `roundquestion`.`question`=?", userID, roundID, questionID)
var count uint32
if err := row.Scan(&count); err != nil {
return false, fmt.Errorf("failed to validate permission to answer question: %v", err)
}
return count == 0, nil
}
......@@ -159,6 +159,9 @@ func (m *mockDuel) InsertTextUserAnswer(roundID, questionID, userID uint32, answ
func (m *mockDuel) IsPlayerOfRound(userID, roundID uint32) (bool, error) {
return false, errors.New("query not mocked")
}
func (m *mockDuel) CanAnswerQuestion(userID, roundID, questionID uint32) (bool, error) {
return false, errors.New("query not mocked")
}
func newMockDuel() mockDuel {
var mock mockDuel
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment