Merge pull request #368 from boreq/create-invite-json

Add a JSON endpoint for generating invites
This commit is contained in:
decentral1se 2022-12-14 10:34:19 +01:00 committed by GitHub
commit 753e30690f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 3 deletions

View File

@ -364,7 +364,7 @@ func New(
m.Get(router.CompleteInviteFacadeFallback).Handler(r.HTML("invite/facade-fallback.tmpl", ih.presentFacadeFallback))
m.Get(router.CompleteInviteInsertID).Handler(r.HTML("invite/insert-id.tmpl", ih.presentInsert))
m.Get(router.CompleteInviteConsume).HandlerFunc(ih.consume)
m.Get(router.OpenModeCreateInvite).HandlerFunc(r.HTML("admin/invite-created.tmpl", ih.createOpenMode))
m.Get(router.OpenModeCreateInvite).HandlerFunc(ih.createOpenMode)
// static assets
m.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(web.Assets)))
@ -379,6 +379,7 @@ func New(
mainMux.Handle("/", m)
consumeURL := urlTo(router.CompleteInviteConsume)
openModeCreateInviteURL := urlTo(router.OpenModeCreateInvite)
// apply HTTP middleware
middlewares := []func(http.Handler) http.Handler{
@ -394,6 +395,10 @@ func New(
next.ServeHTTP(w, csrf.UnsafeSkipCheck(req))
return
}
if req.URL.Path == openModeCreateInviteURL.Path && req.Header.Get("Accept") == "application/json" {
next.ServeHTTP(w, csrf.UnsafeSkipCheck(req))
return
}
next.ServeHTTP(w, req)
})
},

View File

@ -350,7 +350,22 @@ func (html inviteConsumeHTMLResponder) SendError(err error) {
html.renderer.Error(html.rw, html.req, http.StatusInternalServerError, err)
}
func (h inviteHandler) createOpenMode(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
func (h inviteHandler) createOpenMode(rw http.ResponseWriter, r *http.Request) {
switch r.Header.Get("Accept") {
case "application/json":
if r.Method != http.MethodPost {
h.render.Error(rw, r, http.StatusBadRequest, errors.New("invalid method"))
}
h.createOpenModeJSON(rw, r)
default:
if r.Method != http.MethodGet {
h.render.Error(rw, r, http.StatusBadRequest, errors.New("invalid method"))
}
h.render.HTML("admin/invite-created.tmpl", h.createOpenModeHTML)(rw, r)
}
}
func (h inviteHandler) createOpenModeHTML(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
ctx := req.Context()
token, err := h.invites.Create(ctx, -1)
@ -364,3 +379,30 @@ func (h inviteHandler) createOpenMode(rw http.ResponseWriter, req *http.Request)
"FacadeURL": facadeURL.String(),
}, nil
}
func (h inviteHandler) createOpenModeJSON(rw http.ResponseWriter, req *http.Request) {
logger := logging.FromContext(req.Context())
ctx := req.Context()
enc := json.NewEncoder(rw)
token, err := h.invites.Create(ctx, -1)
if err != nil {
data := struct {
Status string `json:"status"`
Error string `json:"error"`
}{"failed", err.Error()}
rw.WriteHeader(http.StatusInternalServerError)
if err := enc.Encode(data); err != nil {
level.Warn(logger).Log("event", "sending json error failed", "err", err)
}
return
}
response := map[string]string{
"url": h.urlTo(router.CompleteInviteFacade, "token", token).String(),
}
if err := enc.Encode(response); err != nil {
level.Warn(logger).Log("event", "sending json response failed", "err", err)
}
}

View File

@ -9,6 +9,7 @@ import (
"encoding/base64"
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
@ -342,3 +343,42 @@ func TestInviteConsumptionDenied(t *testing.T) {
// invite should not be consumed
r.EqualValues(0, ts.InvitesDB.ConsumeCallCount())
}
func TestOpenModeCreateInviteHTML(t *testing.T) {
ts := setup(t)
r := require.New(t)
someToken := "fake-token"
ts.InvitesDB.CreateReturns(someToken, nil)
doc, resp := ts.Client.GetHTML(ts.URLTo(router.OpenModeCreateInvite))
r.Equal(http.StatusOK, resp.Code)
facadeLink := doc.Find("#invite-facade-link")
r.NotNil(facadeLink)
r.Contains(facadeLink.AttrOr("href", ""), someToken)
r.Contains(facadeLink.Text(), someToken)
}
func TestOpenModeCreateInviteJSON(t *testing.T) {
ts := setup(t)
r := require.New(t)
someToken := "fake-token"
ts.InvitesDB.CreateReturns(someToken, nil)
req, err := http.NewRequest("POST", ts.URLTo(router.OpenModeCreateInvite).String(), nil)
r.NoError(err)
req.Header.Set("Accept", "application/json")
recorder := httptest.NewRecorder()
ts.Mux.ServeHTTP(recorder, req)
r.Equal(http.StatusOK, recorder.Code)
response := map[string]string{}
err = json.Unmarshal(recorder.Body.Bytes(), &response)
r.NoError(err)
require.Contains(t, response["url"], someToken)
}

View File

@ -44,7 +44,7 @@ func CompleteApp() *mux.Router {
m.Path("/members/change-password").Methods("GET").Name(MembersChangePasswordForm)
m.Path("/members/change-password").Methods("POST").Name(MembersChangePassword)
m.Path("/create-invite").Methods("GET").Name(OpenModeCreateInvite)
m.Path("/create-invite").Methods("GET", "POST").Name(OpenModeCreateInvite)
m.Path("/join").Methods("GET").Name(CompleteInviteFacade)
m.Path("/join-fallback").Methods("GET").Name(CompleteInviteFacadeFallback)
m.Path("/join-manually").Methods("GET").Name(CompleteInviteInsertID)