diff --git a/internal/models/models.go b/internal/models/models.go index 675c97beb9dede6a5a95ad16527e2365e1c1647a..c5e20768f717a4283c81a2a906123bf125de2489 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -62,15 +62,14 @@ type Area struct { // Section type Section struct { - ID uint `gorm:"primary_key"` - CreatedAt time.Time `json:"-"` - UpdatedAt time.Time `json:"-"` - DeletedAt *time.Time `json:"-"` - AreaID uint - Name string - SeatNumber uint - MapID string - Desks []Desk + ID uint `gorm:"primary_key"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + DeletedAt *time.Time `json:"-"` + AreaID uint + Name string + MapID string + Desks []Desk } // Desk @@ -223,6 +222,8 @@ func (d *DataHandler) ProcessAPI(w http.ResponseWriter, r *http.Request) { d.HandleElection(w, r) case "Area": d.HandleArea(w, r) + case "Section": + d.HandleSection(w, r) } } diff --git a/internal/models/section.go b/internal/models/section.go new file mode 100644 index 0000000000000000000000000000000000000000..debe567e6a41bf693d5e1a0179dd13753d5eb563 --- /dev/null +++ b/internal/models/section.go @@ -0,0 +1,120 @@ +package models + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + + "forge.grandlyon.com/apoyen/elections/internal/auth" +) + +func (d *DataHandler) HandleSection(w http.ResponseWriter, r *http.Request) { + id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/Section/")) + switch method := r.Method; method { + case "GET": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN", "CAPTURER", "VISUALIZER": + d.getSection(w, r, id) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + case "POST": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN": + d.postSection(w, r) + case "CAPTURER", "VISUALIZER": + http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + + case "PUT": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN": + d.putSection(w, r, id) + case "CAPTURER", "VISUALIZER": + http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + case "DELETE": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN": + d.deleteAreaAdmin(w, r, id) + case "CAPTURER", "VISUALIZER": + http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + default: + http.Error(w, "method not allowed", 400) + } +} + +func (d *DataHandler) getSection(w http.ResponseWriter, r *http.Request, id int) { + if id != 0 { + var o Section + if err := d.db.Preload("Desks").First(&o, id).Error; err != nil { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + return + } + json.NewEncoder(w).Encode(o) + } else { + var o []Section + d.db.Preload("Desks").Find(&o) + json.NewEncoder(w).Encode(o) + } +} + +func (d *DataHandler) postSection(w http.ResponseWriter, r *http.Request) { + var o Section + err := json.NewDecoder(r.Body).Decode(&o) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Check that AreaID exist + var area Area + if err := d.db.First(&area, o.AreaID).Error; err != nil { + http.Error(w, ErrorParentNotFound, http.StatusInternalServerError) + return + } + + d.db.Create(&o) + d.db.Last(&o) + json.NewEncoder(w).Encode(o) + +} + +func (d *DataHandler) putSection(w http.ResponseWriter, r *http.Request, id int) { + var o Section + if err := d.db.Preload("Desks").First(&o, id).Error; err != nil { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + return + } + var section Section + err := json.NewDecoder(r.Body).Decode(§ion) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + o.Name = section.Name + o.MapID = section.MapID + d.db.Save(&o) + json.NewEncoder(w).Encode(o) + +} + +func (d *DataHandler) deleteSection(w http.ResponseWriter, r *http.Request, id int) { + if id != 0 { + var o Section + if err := d.db.First(&o, id).Error; err != nil { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + return + } + d.db.Delete(&o) + } else { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + } +} diff --git a/internal/rootmux/admin_test.go b/internal/rootmux/admin_test.go index 3ab03776c06c42858055bbacc22025c266366c24..242760e1de9f6999a7ad126f698825f97deed6bf 100644 --- a/internal/rootmux/admin_test.go +++ b/internal/rootmux/admin_test.go @@ -51,6 +51,17 @@ func AdminTests(t *testing.T) { // Update an area do("PUT", "/api/Area/1", xsrfHeader, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":7,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":7,"MapID":"1","Sections":[]}`) + // Create a Section + do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":null}`) + // Get the section + do("GET", "/api/Section/1", xsrfHeader, ``, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}`) + // Get all the sections + do("GET", "/api/Section/", xsrfHeader, ``, 200, `[{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}]`) + // Update a section + do("PUT", "/api/Section/1", xsrfHeader, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"5"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"5","Desks":[]}`) + + // Delete a section + do("DELETE", "/api/Section/1", xsrfHeader, ``, 200, ``) // Delete an area do("DELETE", "/api/Areas/1", xsrfHeader, ``, 200, ``) // Delete an election diff --git a/internal/rootmux/capturer_test.go b/internal/rootmux/capturer_test.go index 89367267590245bb6395178838a2401143950c5b..c77549f44974af67d3014b4d9b8a36b82976b2d9 100644 --- a/internal/rootmux/capturer_test.go +++ b/internal/rootmux/capturer_test.go @@ -57,6 +57,17 @@ func CapturerTests(t *testing.T) { // Delete an area should fail with 405 do("DELETE", "/api/Area/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) + // Create a section should fail with 405 + do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`) + // Get a section + do("GET", "/api/Section/1", xsrfHeader, "", 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}`) + // Get all the sections + do("GET", "/api/Section/", xsrfHeader, "", 200, `[{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}]`) + // Update a section should fail with 405 + do("PUT", "/api/Section/1", xsrfHeader, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`) + // Delete a section should fail with 405 + do("DELETE", "/api/Section/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) + } // Do a in memory login with an known admin do("POST", "/Login", noH, `{"login": "capturer","password": "password"}`, 200, "") diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go index 62facc32e65584f1cbab90017866450e88ff90a4..61d6a2f31e745e9912808b7ee78c006e0f9f93d5 100644 --- a/internal/rootmux/rootmux_test.go +++ b/internal/rootmux/rootmux_test.go @@ -57,9 +57,9 @@ func TestAll(t *testing.T) { appTests(t) resetData(t) AdminTests(t) - resetData(t) + resetDataWithData(t) CapturerTests(t) - resetData(t) + resetDataWithData(t) VisualizerTests(t) os.RemoveAll("./data") @@ -122,6 +122,35 @@ func resetData(t *testing.T) { do("GET", "/Logout", noH, "", 200, "Logout OK") } +func resetDataWithData(t *testing.T) { + os.RemoveAll("./data") + os.Mkdir("data", os.ModePerm) + os.Create("./data/test.db") + os.Link("../../data/users.db", "./data/users.db") + + ts, do, _ := createTester(t) + defer ts.Close() // Close the tester + + // Init Data in DB tests + init := func() { + response := do("GET", "/api/common/WhoAmI", noH, "", 200, "") + token := auth.TokenData{} + json.Unmarshal([]byte(response), &token) + xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken} + + // Create a capturer + do("POST", "/api/Capturer", xsrfHeader, `{"UserID":2,"Name":"Capturer"}`, 200, `{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}`) + do("POST", "/api/Capturer", xsrfHeader, `{"UserID":3,"Name":"Capturer"}`, 200, `{"ID":2,"UserID":3,"Name":"Capturer","DeskRounds":null}`) + do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct"}`, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":null,"Rounds":null}`) + do("POST", "/api/Area", xsrfHeader, `{"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}`) + do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":null}`) + + } + do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "") + init() + do("GET", "/Logout", noH, "", 200, "Logout OK") +} + func createTester(t *testing.T) (*httptest.Server, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string) { // Create the server mux := CreateRootMux(1443, "../../web") diff --git a/internal/rootmux/visualizer_test.go b/internal/rootmux/visualizer_test.go index 31b5a05ece3d45812b482be5735402a33776e85a..8f436112f549077295cb4a0e9b03fce70498cdff 100644 --- a/internal/rootmux/visualizer_test.go +++ b/internal/rootmux/visualizer_test.go @@ -54,6 +54,17 @@ func VisualizerTests(t *testing.T) { do("PUT", "/api/Area/1", xsrfHeader, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`) // Delete an area should fail with 405 do("DELETE", "/api/Area/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) + + // Create a section should fail with 405 + do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`) + // Get a section + do("GET", "/api/Section/1", xsrfHeader, "", 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}`) + // Get all the sections + do("GET", "/api/Section/", xsrfHeader, "", 200, `[{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}]`) + // Update a section should fail with 405 + do("PUT", "/api/Section/1", xsrfHeader, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`) + // Delete a section should fail with 405 + do("DELETE", "/api/Section/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) } // Do a in memory login with an known admin do("POST", "/Login", noH, `{"login": "visualizer","password": "password"}`, 200, "")