diff --git a/config/config.go b/config/config.go index 2df3b3af..779d79f9 100644 --- a/config/config.go +++ b/config/config.go @@ -3,17 +3,19 @@ package config import ( "fmt" "io/ioutil" - - yaml "gopkg.in/yaml.v2" + "net/url" "git.andreafazzi.eu/andrea/oef/reflect" + yaml "gopkg.in/yaml.v2" ) const ( LOG_LEVEL_OFF = iota LOG_LEVEL_INFO LOG_LEVEL_DEBUG +) +const ( CreateLabel = iota ReadAllLabel ReadLabel @@ -128,6 +130,20 @@ func (pp PathPattern) Path(model string) string { return fmt.Sprintf(pp.PathPattern, model) } -func (c *ConfigT) ReadAllPath(model interface{}) string { - return fmt.Sprintf(c.Handlers.PathPatterns[actions[ReadAllLabel]].PathPattern, reflect.ModelName(model)) +func (c *ConfigT) ReadAllPath(model interface{}, format string) string { + return fmt.Sprintf(c.Handlers.PathPatterns[actions[ReadAllLabel]].PathPattern, reflect.ModelNameLowerPlural(model)) + "?" + c.query(model, format).Encode() +} + +func (c *ConfigT) ReadAllPattern() PathPattern { + return c.Handlers.PathPatterns[actions[ReadAllLabel]] +} + +func (c *ConfigT) query(model interface{}, format string) url.Values { + values := make(url.Values) + + values.Add("format", format) + values.Add("tpl_content", reflect.ModelNameLowerPlural(model)) + values.Add("tpl_layout", "base") // FIXME: use config value + + return values } diff --git a/handlers/handlers.go b/handlers/handlers.go index a90a765e..a7b87a93 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -45,16 +45,14 @@ type Handlers struct { JWTHeaderMiddleware *jwtmiddleware.JWTMiddleware Router *mux.Router -} -var ( permissions map[string]map[string]bool -) - -func init() { - permissions = make(map[string]map[string]bool, 0) } +// var ( +// permissions map[string]map[string]bool +// ) + // Generate CRUD handlers for models func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) { // Install standard paths @@ -86,7 +84,7 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) { )))).Methods(pattern.Methods...) } - // Set permissions for HTML patterns + // Set permissions for role, modelPermissions := range h.Config.Handlers.Permissions { for m, perm := range modelPermissions { @@ -94,19 +92,19 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) { for _, p := range perm { for _, pattern := range h.Config.Handlers.PathPatterns { if pattern.Permission == p { - if permissions[role] == nil { - permissions[role] = make(map[string]bool) + if h.permissions[role] == nil { + h.permissions[role] = make(map[string]bool) } - permissions[role][pattern.Path(reflect.ModelNameLowerPlural(model))] = true + h.permissions[role][pattern.Path(reflect.ModelNameLowerPlural(model))] = true } } for _, pattern := range DefaultAPIPathPatterns { if pattern.Permission == p { - if permissions[role] == nil { - permissions[role] = make(map[string]bool) + if h.permissions[role] == nil { + h.permissions[role] = make(map[string]bool) } - permissions[role][pattern.Path(reflect.ModelNameLowerPlural(model))] = true + h.permissions[role][pattern.Path(reflect.ModelNameLowerPlural(model))] = true } } @@ -149,6 +147,8 @@ func NewHandlers(config *config.ConfigT, renderer map[string]renderer.Renderer, SigningMethod: jwt.SigningMethodHS256, }) + handlers.permissions = make(map[string]map[string]bool) + r := mux.NewRouter() // Authentication @@ -183,9 +183,8 @@ func NewHandlers(config *config.ConfigT, renderer map[string]renderer.Renderer, return handlers } -func (h *Handlers) NewReadAllRequest(model interface{}) (*http.Request, error) { - request, err := http.NewRequest("GET", h.Config.ReadAllPath(reflect.ModelNameLowerPlural(model)), nil) - return request, err +func (h *Handlers) NewReadAllRequest(model interface{}, format string) (*http.Request, error) { + return http.NewRequest("GET", h.Config.ReadAllPath(model, format), nil) } func (h *Handlers) onError(w http.ResponseWriter, r *http.Request, err string) { @@ -243,12 +242,12 @@ func (h *Handlers) setFlashMessage(w http.ResponseWriter, r *http.Request, key s return nil } -func hasPermission(role, path string) bool { - if permissions[role] == nil { +func (h *Handlers) hasPermission(role, path string) bool { + if h.permissions[role] == nil { return false } - return permissions[role][path] + return h.permissions[role][path] } func (h *Handlers) get(w http.ResponseWriter, r *http.Request, model string, pattern config.PathPattern) { @@ -260,11 +259,10 @@ func (h *Handlers) get(w http.ResponseWriter, r *http.Request, model string, pat } else { claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) role := claims["role"].(string) - if !hasPermission(role, pattern.Path(model)) { + if !h.hasPermission(role, pattern.Path(model)) { h.setFlashMessage(w, r, "notAuthorized") h.Renderer[format].Render(w, r, h.CookieStore, fmt.Errorf("%s", "Errore di autorizzazione")) } else { - data, err := getFn(h.Database, mux.Vars(r), w, r) if err != nil { h.Renderer[format].Render(w, r, h.CookieStore, err) @@ -291,7 +289,7 @@ func (h *Handlers) post(w http.ResponseWriter, r *http.Request, model string, pa claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) role := claims["role"].(string) - if !hasPermission(role, pattern.Path(model)) { + if !h.hasPermission(role, pattern.Path(model)) { h.Renderer[respFormat].Render(w, r, h.CookieStore, fmt.Errorf("%s", "Errore di autorizzazione")) } else { data, err = postFn(h.Database, mux.Vars(r), w, r) @@ -320,7 +318,7 @@ func (h *Handlers) delete(w http.ResponseWriter, r *http.Request, model string, claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) role := claims["role"].(string) - if !hasPermission(role, pattern.Path(model)) { + if !h.hasPermission(role, pattern.Path(model)) { h.Renderer[respFormat].Render(w, r, h.CookieStore, fmt.Errorf("%s", "Errore di autorizzazione")) } else { postFn, err := h.Database.GetFunc(pattern.Path(model)) @@ -354,7 +352,7 @@ func (h *Handlers) ReadAll(model interface{}) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { // Replace "api" prefix // pattern.PathPattern = strings.Replace(pattern.PathPattern, "/api", "", -1) - h.get(w, r, model, pattern) + h.get(w, r, reflect.ModelNameLowerPlural(model), h.Config.ReadAllPattern()) } return http.HandlerFunc(fn) diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go index 3cdb3fc2..2c7946db 100644 --- a/handlers/handlers_test.go +++ b/handlers/handlers_test.go @@ -3,11 +3,9 @@ package handlers import ( "context" "encoding/json" - "fmt" "log" "net/http" "net/http/httptest" - "strings" "testing" "time" @@ -31,9 +29,11 @@ type testSuite struct { prettytest.Suite } -func authenticate(request *http.Request, tokenString string, signingKey string) (*http.Request, error) { +func login(request *http.Request, handlers *Handlers, username string, password string) (*http.Request, error) { + + tokenString := requestToken(handlers, username, password) token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - return []byte(signingKey), nil + return []byte(handlers.Config.Keys.JWTSigningKey), nil }) if err != nil { return nil, err @@ -44,17 +44,17 @@ func authenticate(request *http.Request, tokenString string, signingKey string) } -func requestToken(db *orm.Database, handlers *Handlers) string { +func requestToken(handlers *Handlers, username string, password string) string { req, err := http.NewRequest("GET", "/get_token", nil) if err != nil { panic(err) } - req.SetBasicAuth("admin", "admin") + req.SetBasicAuth(username, password) rr := httptest.NewRecorder() - handlers.GetToken(db, []byte(db.Config.Keys.JWTSigningKey)).ServeHTTP(rr, req) + handlers.GetToken(handlers.Database, []byte(handlers.Config.Keys.JWTSigningKey)).ServeHTTP(rr, req) var data struct { Token string @@ -95,7 +95,7 @@ func (t *testSuite) BeforeAll() { for !connected { var err error - time.Sleep(5 * time.Second) + time.Sleep(2 * time.Second) db, err = orm.NewDatabase(conf, orm.Models) if err != nil { @@ -148,46 +148,27 @@ func (t *testSuite) BeforeAll() { conf.Handlers.APIPathPatterns = DefaultAPIPathPatterns handlers = NewHandlers(conf, renderer, db, orm.Models) - token = requestToken(db, handlers) } func (t *testSuite) TestReadAllContests() { - req, err := handlers.NewReadAllRequest(&orm.Contest{}) - - if err != nil { - panic(err) - } - - pattern := config.PathPattern{ - "/%s", - "/%s?format=html&tpl_layout=base&tpl_content=%s", - []string{"GET"}, PermissionReadAll, - } - - rr := httptest.NewRecorder() - - req, err = authenticate(req, token, conf.Keys.JWTSigningKey) + req, err := handlers.NewReadAllRequest(&orm.Contest{}, "html") t.Nil(err) - if err != nil { - handlers.ReadAll(&Contest{}).ServeHTTP(rr, req) + req, err = login(req, handlers, "admin", "admin") + t.Nil(err) + if !t.Failed() { + rr := httptest.NewRecorder() + + handlers.ReadAll(&orm.Contest{}).ServeHTTP(rr, req) t.Equal(http.StatusOK, rr.Code) if !t.Failed() { - doc, err := goquery.NewDocumentFromResponse(rr.Result()) + doc, err := goquery.NewDocumentFromReader(rr.Body) if err != nil { log.Fatal(err) } - - // Find the review items - doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) { - // For each item found, get the band and title - band := s.Find("a").Text() - title := s.Find("i").Text() - fmt.Printf("Review %d: %s - %s\n", i, band, title) - }) - t.True(strings.Contains(rr.Body.String(), "JUNIOR Contest")) + t.Equal(2, doc.Find(".list-group-item").Size()) } } diff --git a/orm/contest.go b/orm/contest.go index 308b1a66..8530de45 100644 --- a/orm/contest.go +++ b/orm/contest.go @@ -92,7 +92,7 @@ func (c *Contest) ReadAll(db *Database, args map[string]string, w http.ResponseW var contests []*Contest claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) - + if claims["admin"].(bool) { if err := db._db.Order("created_at").Find(&contests).Error; err != nil { return nil, err