From 5385ef65bb86fccb5864e6bd7e3d93d0f80348cb Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 10 May 2021 09:06:47 +0200 Subject: [PATCH] add middleware to check roles fixes #176 --- web/handlers/admin/aliases.go | 2 +- web/handlers/admin/handler.go | 39 +++++++++++++++++++++++++++++++---- web/handlers/notices_test.go | 24 ++++++++++++++++----- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/web/handlers/admin/aliases.go b/web/handlers/admin/aliases.go index 5cfe30c..fdf17b4 100644 --- a/web/handlers/admin/aliases.go +++ b/web/handlers/admin/aliases.go @@ -92,7 +92,7 @@ func (h aliasesHandler) revoke(rw http.ResponseWriter, req *http.Request) { return } - status := http.StatusTemporaryRedirect + status := http.StatusTemporaryRedirect // TODO: should be SeeOther because it's method POST coming in err = h.db.Revoke(ctx, aliasName) if err != nil { h.flashes.AddError(rw, req, err) diff --git a/web/handlers/admin/handler.go b/web/handlers/admin/handler.go index 80053ba..02b2db2 100644 --- a/web/handlers/admin/handler.go +++ b/web/handlers/admin/handler.go @@ -22,6 +22,7 @@ import ( "github.com/ssb-ngi-pointer/go-ssb-room/web" weberrors "github.com/ssb-ngi-pointer/go-ssb-room/web/errors" "github.com/ssb-ngi-pointer/go-ssb-room/web/i18n" + "github.com/ssb-ngi-pointer/go-ssb-room/web/members" "github.com/ssb-ngi-pointer/go-ssb-room/web/router" ) @@ -155,10 +156,11 @@ func Handler( noticeDB: dbs.Notices, pinnedDB: dbs.PinnedNotices, } - mux.HandleFunc("/notice/edit", r.HTML("admin/notice-edit.tmpl", nh.edit)) - mux.HandleFunc("/notice/translation/draft", r.HTML("admin/notice-edit.tmpl", nh.draftTranslation)) - mux.HandleFunc("/notice/translation/add", nh.addTranslation) - mux.HandleFunc("/notice/save", nh.save) + onlyModsAndAdmins := checkMemberRole(r.Error, roomdb.RoleModerator, roomdb.RoleAdmin) + mux.Handle("/notice/edit", onlyModsAndAdmins(r.HTML("admin/notice-edit.tmpl", nh.edit))) + mux.Handle("/notice/translation/draft", onlyModsAndAdmins(r.HTML("admin/notice-edit.tmpl", nh.draftTranslation))) + mux.Handle("/notice/translation/add", onlyModsAndAdmins(http.HandlerFunc(nh.addTranslation))) + mux.Handle("/notice/save", onlyModsAndAdmins(http.HandlerFunc(nh.save))) // path:/ matches everything that isn't registerd (ie. its the "Not Found handler") mux.HandleFunc("/", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -168,6 +170,35 @@ func Handler( return customStripPrefix("/admin", mux) } +func checkMemberRole(eh render.ErrorHandlerFunc, roles ...roomdb.Role) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + currentMember := members.FromContext(req.Context()) + if currentMember == nil { + err := weberrors.ErrRedirect{Path: "/admin/dashboard", Reason: fmt.Errorf("not an member")} + eh(rw, req, http.StatusSeeOther, err) + return + } + + var roleMatched = false + for _, r := range roles { + if currentMember.Role == r { + roleMatched = true + break + } + } + + if !roleMatched { + err := weberrors.ErrRedirect{Path: "/admin/dashboard", Reason: weberrors.ErrNotAuthorized} + eh(rw, req, http.StatusSeeOther, err) + return + } + + next.ServeHTTP(rw, req) + }) + } +} + // how many elements does a paginated page have by default const defaultPageSize = 20 diff --git a/web/handlers/notices_test.go b/web/handlers/notices_test.go index 23362ec..9296603 100644 --- a/web/handlers/notices_test.go +++ b/web/handlers/notices_test.go @@ -190,18 +190,32 @@ func TestNoticesCreateOnlyModsAndHigher(t *testing.T) { resp = ts.Client.PostForm(postEndpoint, loginVals) a.Equal(http.StatusSeeOther, resp.Code, "wrong HTTP status code for sign in") - cnt := ts.MembersDB.GetByIDCallCount() - // now we are logged in, but we shouldn't be able to get the draft page doc, resp = ts.Client.GetHTML(draftNotice) - a.Equal(http.StatusTemporaryRedirect, resp.Code) + a.Equal(http.StatusSeeOther, resp.Code) dashboardURL := ts.URLTo(router.AdminDashboard) a.Equal(dashboardURL.Path, resp.Header().Get("Location")) a.True(len(resp.Result().Cookies()) > 0, "got a cookie") - webassert.HasFlashMessages(t, ts.Client, dashboardURL, "AdminMemberDetailsAliasRevoked") + webassert.HasFlashMessages(t, ts.Client, dashboardURL, "ErrorNotAuthorized") - a.Equal(cnt+1, ts.MembersDB.GetByIDCallCount()) + // also shouldnt be allowed to save/post + id := []string{"1"} + title := []string{"SSB Breaking News: This Test Is Great"} + content := []string{"Absolutely Thrilling Content"} + language := []string{"en-GB"} + + // POST a correct request to the save handler, and verify that the save was handled using the mock database) + u := ts.URLTo(router.AdminNoticeSave) + formValues := url.Values{"id": id, "title": title, "content": content, "language": language, csrfName: []string{csrfValue}} + resp = ts.Client.PostForm(u, formValues) + a.Equal(http.StatusSeeOther, resp.Code, "POST should work") + a.Equal(0, ts.NoticeDB.SaveCallCount(), "noticedb should not save the notice") + + a.Equal(dashboardURL.Path, resp.Header().Get("Location")) + a.True(len(resp.Result().Cookies()) > 0, "got a cookie") + + webassert.HasFlashMessages(t, ts.Client, dashboardURL, "ErrorNotAuthorized") }