Protect responses from participants

This commit is contained in:
Andrea Fazzi 2020-02-05 10:10:27 +01:00
parent 6c1550e7fe
commit b06eba6698
8 changed files with 122 additions and 3 deletions

View file

@ -146,6 +146,10 @@ func (c *ConfigT) CreatePath(model interface{}, path, format string) string {
return path + "?" + c.query(model, CreateLabel, format).Encode() return path + "?" + c.query(model, CreateLabel, format).Encode()
} }
func (c *ConfigT) UpdatePath(model interface{}, path, format string) string {
return path + "?" + c.query(model, UpdateLabel, format).Encode()
}
func (c *ConfigT) DeletePath(model interface{}, path, format string) string { func (c *ConfigT) DeletePath(model interface{}, path, format string) string {
return path + "?format=html" return path + "?format=html"
} }
@ -162,6 +166,10 @@ func (c *ConfigT) ReadPattern() PathPattern {
return c.Handlers.PathPatterns[actions[ReadLabel]] return c.Handlers.PathPatterns[actions[ReadLabel]]
} }
func (c *ConfigT) UpdatePattern() PathPattern {
return c.Handlers.PathPatterns[actions[UpdateLabel]]
}
func (c *ConfigT) DeletePattern() PathPattern { func (c *ConfigT) DeletePattern() PathPattern {
return c.Handlers.PathPatterns[actions[DeleteLabel]] return c.Handlers.PathPatterns[actions[DeleteLabel]]
} }
@ -180,6 +188,9 @@ func (c *ConfigT) query(model interface{}, action int, format string) url.Values
tplContent = reflect.ModelNameLowerPlural(model) tplContent = reflect.ModelNameLowerPlural(model)
case ReadLabel: case ReadLabel:
tplContent = reflect.ModelNameLowerPlural(model) + "_show" tplContent = reflect.ModelNameLowerPlural(model) + "_show"
case UpdateLabel:
tplContent = reflect.ModelNameLowerPlural(model) + "_add_update"
values.Add("update", "true")
} }
values.Add("tpl_content", tplContent) values.Add("tpl_content", tplContent)

View file

@ -19,7 +19,10 @@ func (e *Error) Error() string {
var ( var (
RecordExists = errors.New("Record already exists!") RecordExists = errors.New("Record already exists!")
NotAuthorized = errors.New(i18n.Authorization["notAuthorized"]["it"]) NotAuthorized = &Error{
TemplateName: "error_not_authorized",
Err: errors.New(i18n.Authorization["notAuthorized"]["it"]),
}
SchoolExists = &Error{ SchoolExists = &Error{
TemplateName: "error_school_exists", TemplateName: "error_school_exists",

View file

@ -206,6 +206,20 @@ func (h *Handlers) NewReadRequest(model interface{}, path string, format string)
return http.NewRequest("GET", h.Config.ReadPath(model, path, format), nil) return http.NewRequest("GET", h.Config.ReadPath(model, path, format), nil)
} }
func (h *Handlers) NewUpdateRequest(model interface{}, path string, format string, method string, form url.Values) (*http.Request, error) {
var (
request *http.Request
err error
)
switch method {
case "GET":
request, err = http.NewRequest("GET", h.Config.UpdatePath(model, path, format), nil)
case "POST":
request, err = http.NewRequest("POST", h.Config.UpdatePath(model, path, format), strings.NewReader(form.Encode()))
}
return request, err
}
func (h *Handlers) NewCreateRequest(model interface{}, path string, format string, method string, form url.Values) (*http.Request, error) { func (h *Handlers) NewCreateRequest(model interface{}, path string, format string, method string, form url.Values) (*http.Request, error) {
var ( var (
request *http.Request request *http.Request
@ -401,6 +415,26 @@ func (h *Handlers) Create(model interface{}) http.Handler {
return newRootMiddleware(h, fn) return newRootMiddleware(h, fn)
} }
func (h *Handlers) Update(model interface{}) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) error {
switch r.Method {
case "GET":
err := h.get(w, r, reflect.ModelNameLowerPlural(model), h.Config.UpdatePattern())
if err != nil {
return err
}
case "POST":
err := h.post(w, r, reflect.ModelNameLowerPlural(model), h.Config.UpdatePattern())
if err != nil {
return err
}
}
return nil
}
return newRootMiddleware(h, fn)
}
func (h *Handlers) ReadAll(model interface{}) http.Handler { func (h *Handlers) ReadAll(model interface{}) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) { fn := func(w http.ResponseWriter, r *http.Request) {
h.get(w, r, reflect.ModelNameLowerPlural(model), h.Config.ReadAllPattern()) h.get(w, r, reflect.ModelNameLowerPlural(model), h.Config.ReadAllPattern())

View file

@ -176,7 +176,7 @@ func (t *testSuite) BeforeAll() {
panic(err) panic(err)
} }
conf.LogLevel = config.LOG_LEVEL_OFF conf.LogLevel = config.LOG_LEVEL_DEBUG
// Initialize the ORM // Initialize the ORM
@ -443,6 +443,61 @@ func (t *testSuite) TestSchoolSubscription() {
} }
func (t *testSuite) TestParticipantResponse() {
form := url.Values{}
form.Set("Singleresponses.0", "9")
req, err := handlers.NewCreateRequest(&orm.School{}, "/responses/5/update", "html", "POST", form)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
t.Nil(err)
req, err = login(req, handlers, "RHOMAT9HZ", "9HzXOfJPje")
t.Nil(err)
if !t.Failed() {
rr := httptest.NewRecorder()
router := mux.NewRouter()
router.Handle("/responses/{id}/update", handlers.Update(&orm.Response{}))
router.ServeHTTP(rr, req)
t.Equal(http.StatusSeeOther, rr.Code)
if !t.Failed() {
req, err := handlers.NewReadRequest(&orm.Contest{}, "/responses/1", "html")
t.Nil(err)
req, err = login(req, handlers, "admin", "admin")
t.Nil(err)
rr = httptest.NewRecorder()
router = mux.NewRouter()
router.Handle("/responses/{id}", handlers.Read(&orm.Response{}))
router.ServeHTTP(rr, req)
t.Equal(http.StatusOK, rr.Code)
log.Println(rr)
if !t.Failed() {
doc, err := goquery.NewDocumentFromReader(rr.Body)
if err != nil {
log.Fatal(err)
}
expected := "1"
ok := true
doc.Find("h1").Each(func(i int, s *goquery.Selection) {
if !strings.Contains(s.Text(), expected) {
ok = false
}
})
t.True(ok)
}
}
}
}
func (t *testSuite) TestUserModifier() { func (t *testSuite) TestUserModifier() {
id, err := subscribeSchoolAsSchool() id, err := subscribeSchoolAsSchool()
t.Nil(err) t.Nil(err)

View file

@ -27,7 +27,7 @@ var (
"it": "Il tempo utile per consegnare la prova è scaduto.", "it": "Il tempo utile per consegnare la prova è scaduto.",
}, },
"notAuthorized": map[string]string{ "notAuthorized": map[string]string{
"it": "Non si è autorizzati ad accedere a questa pagina", "it": "Non si è autorizzati ad accedere a questa pagina.",
}, },
"schoolExists": map[string]string{ "schoolExists": map[string]string{
"it": "Una scuola con questo codice meccanografico è già presente nella base dati!", "it": "Una scuola con questo codice meccanografico è già presente nella base dati!",

View file

@ -261,6 +261,12 @@ func (model *Participant) Read(db *Database, args map[string]string, w http.Resp
id := args["id"] id := args["id"]
if isParticipant(r) {
if id != getModelIDFromToken(r) {
return nil, errors.NotAuthorized
}
}
// School user can access to its participants only! // School user can access to its participants only!
if isSchool(r) { if isSchool(r) {
if err := db._db.Preload("School").First(&participant, id).Error; err != nil { if err := db._db.Preload("School").First(&participant, id).Error; err != nil {

View file

@ -141,6 +141,12 @@ func (model *Response) Read(db *Database, args map[string]string, w http.Respons
return nil, err return nil, err
} }
if isParticipant(r) {
if strconv.Itoa(int(response.ParticipantID)) != getModelIDFromToken(r) {
return nil, errors.NotAuthorized
}
}
if response.AnswersIDs != "" { if response.AnswersIDs != "" {
srIDs := strings.Split(response.AnswersIDs, " ") srIDs := strings.Split(response.AnswersIDs, " ")
for _, srID := range srIDs { for _, srID := range srIDs {

View file

@ -0,0 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore di autorizzazione"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{end}}