Merge branch 'fix_errors'

This commit is contained in:
Andrea Fazzi 2020-02-19 14:57:56 +01:00
commit f09fc930c1
19 changed files with 198 additions and 126 deletions

View file

@ -1 +1 @@
0.4.2-7-gf33e5bc-master
0.4.2-10-g9d99f56-fix_errors

View file

@ -1,7 +1,7 @@
url: "http://localhost:3000"
log_level: 2
language: "it"
jwt_expire_time: 1
jwt_expire_time: 60
keys:
cookie_store_key: "something-very-secret"

View file

@ -10,6 +10,7 @@ type Error struct {
TemplateName string
Err error
Referer string
Title string
}
func (e *Error) Error() string {
@ -20,6 +21,7 @@ var (
RecordExists = errors.New("Record already exists!")
NotAuthorized = &Error{
Title: "Errore di autenticazione",
TemplateName: "error_not_authorized",
Err: errors.New(i18n.Authorization["notAuthorized"]["it"]),
}

View file

@ -47,10 +47,10 @@ type Handlers struct {
Renderer map[string]renderer.Renderer
Login func(db *orm.Database, store *sessions.CookieStore, signingKey []byte) http.Handler
Login func(db *orm.Database, store *sessions.CookieStore, signingKey []byte) handlerFuncWithError
Logout func(store *sessions.CookieStore) http.Handler
Home func() http.Handler
GetToken func(db *orm.Database, signingKey []byte) http.Handler
GetToken func(db *orm.Database, signingKey []byte) handlerFuncWithError
Static func() http.Handler
Recover func(next http.Handler) http.Handler
@ -174,12 +174,12 @@ func NewHandlers(config *config.ConfigT, renderer map[string]renderer.Renderer,
// Authentication
r.Handle("/login", handlers.Login(handlers.Database, handlers.CookieStore, []byte(config.Keys.JWTSigningKey)))
r.Handle("/login", newRootMiddleware(handlers, handlers.Login(handlers.Database, handlers.CookieStore, []byte(config.Keys.JWTSigningKey))))
r.Handle("/logout", handlers.Logout(handlers.CookieStore))
// School subscription
r.Handle("/subscribe", handlers.Login(handlers.Database, handlers.CookieStore, []byte(config.Keys.JWTSigningKey)))
r.Handle("/subscribe", newRootMiddleware(handlers, handlers.Login(handlers.Database, handlers.CookieStore, []byte(config.Keys.JWTSigningKey))))
// Captcha
@ -197,7 +197,7 @@ func NewHandlers(config *config.ConfigT, renderer map[string]renderer.Renderer,
// Token handling
r.Handle("/get_token", handlers.GetToken(handlers.Database, []byte(config.Keys.JWTSigningKey)))
r.Handle("/get_token", newRootMiddleware(handlers, handlers.GetToken(handlers.Database, []byte(config.Keys.JWTSigningKey))))
// Static file server
@ -270,7 +270,6 @@ func (h *Handlers) cookieExtractor(r *http.Request) (string, error) {
if err != nil {
return "", nil
}
if session.Values["token"] == nil {
return "", nil
}
@ -344,7 +343,10 @@ func (h *Handlers) get(w http.ResponseWriter, r *http.Request, model string, pat
return err
}
format := r.URL.Query().Get("format")
h.Renderer[format].Render(w, r, h.CookieStore, data, r.URL.Query())
err = h.Renderer[format].Render(w, r, h.CookieStore, data, r.URL.Query())
if err != nil {
return err
}
return nil
}
@ -363,7 +365,10 @@ func (h *Handlers) post(w http.ResponseWriter, r *http.Request, model string, pa
}
} else {
format := renderer.GetContentFormat(r)
h.Renderer[format].Render(w, r, h.CookieStore, data.(orm.IDer).GetID())
err := h.Renderer[format].Render(w, r, h.CookieStore, data.(orm.IDer).GetID())
if err != nil {
return err
}
}
return nil
@ -385,7 +390,10 @@ func (h *Handlers) delete(w http.ResponseWriter, r *http.Request, model string,
json.NewEncoder(w).Encode(data)
} else {
format := renderer.GetContentFormat(r)
h.Renderer[format].Render(w, r, h.CookieStore, data.(orm.IDer).GetID())
err := h.Renderer[format].Render(w, r, h.CookieStore, data.(orm.IDer).GetID())
if err != nil {
return err
}
}
return nil
@ -399,10 +407,12 @@ func respondWithError(h *Handlers, w http.ResponseWriter, r *http.Request, err e
format = renderer.GetContentFormat(r)
}
if h.Config.LogLevel > config.LOG_LEVEL_OFF {
log.Println(err)
log.Println("Error:", err)
}
// FIXME: this call could be superflous when an error occurs
// in a template execution
w.WriteHeader(http.StatusInternalServerError)
h.Renderer[format].Render(w, r, h.CookieStore, err)
h.Renderer[format].WriteError(w, r, err)
}
func (h *Handlers) Create(model interface{}) http.Handler {
@ -550,10 +560,6 @@ func (m *rootMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// This is where our error handling logic starts.
if m.h.Config.LogLevel > config.LOG_LEVEL_OFF {
log.Printf("An error accured: %v", err) // Log the error.
}
if err, ok := err.(*errors.Error); ok {
err.Referer = r.Header.Get("Referer")
}

View file

@ -7,6 +7,7 @@ import (
"strconv"
"time"
oef_errors "git.andreafazzi.eu/andrea/oef/errors"
"git.andreafazzi.eu/andrea/oef/orm"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gorilla/sessions"
@ -24,25 +25,36 @@ type UserToken struct {
UserID string
}
func clearSession(response http.ResponseWriter) {
cookie := &http.Cookie{
Name: "login-session",
Value: "",
Path: "/",
MaxAge: -1,
}
http.SetCookie(response, cookie)
}
func DefaultLogoutHandler(store *sessions.CookieStore) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, "login-session")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Values["token"] = []uint8{}
session.Save(r, w)
// session, err := store.Get(r, "login-session")
// if err != nil {
// // http.Error(w, err.Error(), http.StatusInternalServerError)
// store.Set(w)
// return
// }
// session.Values["token"] = []uint8{}
// session.Save(r, w)
clearSession(w)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
return http.HandlerFunc(fn)
}
func (h *Handlers) DefaultLoginHandler(db *orm.Database, store *sessions.CookieStore, signingKey []byte) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
func (h *Handlers) DefaultLoginHandler(db *orm.Database, store *sessions.CookieStore, signingKey []byte) handlerFuncWithError {
fn := func(w http.ResponseWriter, r *http.Request) error {
if r.Method == "GET" {
h.Renderer["html"].Render(w, r, store, nil, r.URL.Query())
}
@ -54,7 +66,7 @@ func (h *Handlers) DefaultLoginHandler(db *orm.Database, store *sessions.CookieS
} else {
session, err := store.Get(r, "login-session")
if err != nil {
panic(err)
return oef_errors.NotAuthorized
}
session.Values["token"] = token
@ -63,8 +75,9 @@ func (h *Handlers) DefaultLoginHandler(db *orm.Database, store *sessions.CookieS
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
return nil
}
return http.HandlerFunc(fn)
return handlerFuncWithError(fn)
}
// FIXME: This is an hack for fast prototyping: users should have
@ -137,18 +150,19 @@ func getToken(db *orm.Database, username string, password string, signingKey []b
}
// FIXME: Refactor the functions above please!!!
func DefaultGetTokenHandler(db *orm.Database, signingKey []byte) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
func DefaultGetTokenHandler(db *orm.Database, signingKey []byte) handlerFuncWithError {
fn := func(w http.ResponseWriter, r *http.Request) error {
username, password, _ := r.BasicAuth()
token, err := getToken(db, username, password, signingKey)
if err != nil {
panic(err)
return err
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write([]byte(fmt.Sprintf("{\"Token\":\"%s\"}", string(token))))
return nil
}
return http.HandlerFunc(fn)
return handlerFuncWithError(fn)
}

View file

@ -18,8 +18,7 @@ import (
"strings"
"time"
"git.andreafazzi.eu/andrea/oef/errors"
oef_errors "git.andreafazzi.eu/andrea/oef/errors"
"github.com/gocarina/gocsv"
"github.com/gorilla/schema"
"github.com/gorilla/sessions"
@ -29,6 +28,7 @@ import (
type Renderer interface {
Render(http.ResponseWriter, *http.Request, *sessions.CookieStore, interface{}, ...url.Values) error
WriteError(http.ResponseWriter, *http.Request, interface{})
}
type JSONRenderer struct{}
@ -36,10 +36,9 @@ type CSVRenderer struct{}
type PDFRenderer struct{}
type htmlTemplateData struct {
Data interface{}
Options url.Values
Claims jwt.MapClaims
FlashMessages []interface{}
Data interface{}
Options url.Values
Claims jwt.MapClaims
}
type JsonResponse struct {
@ -70,6 +69,10 @@ func NewJSONRenderer() (*JSONRenderer, error) {
return &JSONRenderer{}, nil
}
func (rend *JSONRenderer) WriteError(w http.ResponseWriter, r *http.Request, data interface{}) {
}
func (rend *JSONRenderer) Render(w http.ResponseWriter, r *http.Request, store *sessions.CookieStore, data interface{}, options ...url.Values) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if isErrorType(data) {
@ -181,9 +184,16 @@ func NewHTMLRenderer(templatePath string) (*HTMLRenderer, error) {
}
func (rend *HTMLRenderer) writeError(w http.ResponseWriter, r *http.Request, data interface{}) {
var t *template.Template
var (
t *template.Template
claims jwt.MapClaims
)
err, ok := data.(*htmlTemplateData).Data.(*errors.Error)
if r.Context().Value("user") != nil {
claims = r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
}
err, ok := data.(*oef_errors.Error)
if ok {
t, ok = rend.templates[err.TemplateName]
if !ok {
@ -191,7 +201,6 @@ func (rend *HTMLRenderer) writeError(w http.ResponseWriter, r *http.Request, dat
}
} else {
t, ok = rend.templates["error"]
if !ok {
panic(fmt.Errorf("Error template not found! Can't proceed, sorry."))
@ -199,12 +208,25 @@ func (rend *HTMLRenderer) writeError(w http.ResponseWriter, r *http.Request, dat
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
e := t.ExecuteTemplate(w, "base", data)
if e != nil {
panic(e)
if claims != nil {
e := t.ExecuteTemplate(w, "base", &htmlTemplateData{data, nil, claims})
if e != nil {
panic(e)
}
} else {
e := t.ExecuteTemplate(w, "error", &htmlTemplateData{data, nil, nil})
if e != nil {
panic(e)
}
}
}
func (rend *HTMLRenderer) WriteError(w http.ResponseWriter, r *http.Request, data interface{}) {
rend.writeError(w, r, data)
}
func (rend *HTMLRenderer) Render(w http.ResponseWriter, r *http.Request, store *sessions.CookieStore, data interface{}, options ...url.Values) error {
var claims jwt.MapClaims
@ -212,48 +234,24 @@ func (rend *HTMLRenderer) Render(w http.ResponseWriter, r *http.Request, store *
claims = r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
}
if data != nil && isErrorType(data) {
session, err := store.Get(r, "flash-session")
if err != nil {
rend.writeError(w, r, &htmlTemplateData{err, nil, claims, nil})
return err
}
fm := session.Flashes()
err = session.Save(r, w)
if err != nil {
rend.writeError(w, r, &htmlTemplateData{err, nil, claims, fm})
return err
}
rend.writeError(w, r, &htmlTemplateData{data.(error), nil, claims, fm})
if err, ok := data.(*oef_errors.Error); ok {
// rend.writeError(w, r, &htmlTemplateData{data, nil, claims})
return err
} else {
t, ok := rend.templates[options[0]["tpl_content"][0]]
if !ok {
err := fmt.Errorf("Template %s not found", options[0]["tpl_content"][0])
rend.writeError(w, r, &htmlTemplateData{err, nil, claims, nil})
return err
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
session, err := store.Get(r, "flash-session")
if err != nil {
rend.writeError(w, r, &htmlTemplateData{err, nil, claims, nil})
return err
}
fm := session.Flashes()
err = session.Save(r, w)
if err != nil {
rend.writeError(w, r, &htmlTemplateData{err, nil, claims, fm})
return err
}
err = t.ExecuteTemplate(w, options[0]["tpl_layout"][0], &htmlTemplateData{data, options[0], claims, fm})
if err != nil {
rend.writeError(w, r, &htmlTemplateData{err, nil, claims, fm})
return err
}
}
t, ok := rend.templates[options[0]["tpl_content"][0]]
if !ok {
err := fmt.Errorf("Template %s not found", options[0]["tpl_content"][0])
// rend.writeError(w, r, &htmlTemplateData{err, nil, claims})
return err
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := t.ExecuteTemplate(w, options[0]["tpl_layout"][0], &htmlTemplateData{data, options[0], claims}); err != nil {
// rend.writeError(w, r, &htmlTemplateData{err, nil, claims})
return err
}
return nil
}

View file

@ -1,4 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore durante l'iscrizione della scuola"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{$options := `title: "Errore durante l'iscrizione di un partecipante"`}}
{{template "show_error" dict "options" ($options|yaml) "data" .Data}}
{{end}}

View file

@ -1,4 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore nella creazione/aggiornamento di un partecipante"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{template "show_error" dict "options" ($options|yaml) "data" .Data}}
{{end}}

View file

@ -1,4 +1,13 @@
{{ define "content" }}
{{$options := `title: "Errore di autorizzazione"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
<div class="container">
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">
Errore di autorizzazione
</h4>
{{.Data}}
Clicca {{"/logout"|alertLink "qui"}} per effettuare il logout e ritornare alla pagina di autenticazione.
</div>
<p>
</p>
</div>
{{end}}

View file

@ -1,4 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore nella gestione della prova di gara"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{template "show_error" dict "options" ($options|yaml) "data" .Data}}
{{end}}

View file

@ -1,4 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore nella creazione/aggiornamento di un partecipante"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{template "show_error" dict "options" ($options|yaml) "data" .Data}}
{{end}}

View file

@ -1,4 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore nell'apertura della prova di gara"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{template "show_error" dict "options" ($options|yaml) "data" .Data}}
{{end}}

View file

@ -1,4 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore nella creazione/aggiornamento di una scuola"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{template "show_error" dict "options" ($options|yaml) "data" .Data}}
{{end}}

View file

@ -1,4 +1,4 @@
{{ define "content" }}
{{$options := `title: "Errore durante l'iscrizione di una scuola"`}}
{{template "error" dict "options" ($options|yaml) "data" .Data}}
{{template "show_error" dict "options" ($options|yaml) "data" .Data}}
{{end}}

View file

@ -61,16 +61,6 @@
</ul>
</div><!--/.nav-collapse -->
</nav>
{{- if .FlashMessages -}}
{{range $message := .FlashMessages}}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Attenzione!</strong> {{$message}}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{{end}}
{{- end -}}
<div class="base-template">
{{ template "content" . }}
</div>

View file

@ -1,15 +1,55 @@
{{define "error"}}
<div class="container">
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">
{{.options.title}}
</h4>
{{.data}}
{{if .data.Referer}}
Clicca {{.data.Referer|alertLink "qui"}} per tornare all'azione precedente.
{{end}}
</div>
<p>
</p>
</div>
{{- define "error" -}}
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.css">
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-select@1.13.9/dist/css/bootstrap-select.min.css">
<link rel="stylesheet" href="/styles.css" />
<title>Olimpiadi di Economia e Finanza - Piattaforma di gara</title>
</head>
<body data-spy="scroll">
<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-primary">
{{- $homeURL := "" -}}
<a class="navbar-brand" href="{{$homeURL}}">
<span class="fa fa-landmark"></span>
OEF 2020
</a>
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="base-template">
{{ template "content" . }}
</div>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous">
</script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous">
</script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-select@1.13.9/dist/js/bootstrap-select.min.js"></script>
<script src="/main.bundle.js"></script>
</body>
</html>
{{end}}

View file

@ -0,0 +1,15 @@
{{define "show_error"}}
<div class="container">
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">
{{.options.title}}
</h4>
{{.data}}
{{if .data.Referer}}
Clicca {{.data.Referer|alertLink "qui"}} per tornare all'azione precedente.
{{end}}
</div>
<p>
</p>
</div>
{{end}}

View file

@ -127,6 +127,7 @@
<div class="row">
<div class="col-md-12">
<h2 class="karmen-relation-header">Suggerimenti</h2>
{{if .Data.School}}
{{if le (len .Data.School.Participants) 1}}
<p>
E' possibile iscrivere fino a due partecipanti alla gara (di diversa categoria).
@ -141,7 +142,7 @@
<strong>Iscrizione completa.</strong> Non è possibile aggiungere ulteriori partecipanti.
</div>
{{end}}
{{end}}
<div class="alert alert-danger text-justify">
<span class="fa
fa-exclamation-triangle mr-1">
@ -155,9 +156,9 @@
</div>
</div>
</div>
</div>
{{end}}
</div>
{{end}}
</div>

View file

@ -6,9 +6,6 @@
<div class="container">
{{if $isSubscriber}}
{{if .FlashMessages}}
<p>Si è verificato un errore. Clicca <a href="/logout">qui</a> per uscire da questa sessione.</p>
{{else}}
<p>
Grazie per aver iscritto la scuola <strong>{{.Data.Name}}</strong>
alle Olimpiadi di Economia e Finanza.
@ -27,7 +24,7 @@
utilizzare le credenziali ricevute per iscrivere gli studenti alla
competizione.
</p>
{{end}}
{{else}}
{{$deletePath := ""}}