Add a JSON endpoint for generating invites
When running in open mode invites can be freely generated by accessing /create-invite. This displays an HTML page which creates and displays an invite to the user. This commit adds an additional way of creating invites in open mode. A POST request can be sent to the same /create-invite endpoint with the Accept header set to application/json. This returns a JSON response which contains an invite url. The purpose of this change is to make automatic invite generation easier in SSB clients.
This commit is contained in:
parent
0a5b6d13e4
commit
ab664aafc3
@ -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)
|
||||
})
|
||||
},
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user