alias JSON endpoint and testing
This commit is contained in:
parent
50e4ebbaca
commit
b9bcbb42ec
|
@ -0,0 +1,150 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"go.mindeco.de/http/render"
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/aliases"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
)
|
||||
|
||||
type aliasHandler struct {
|
||||
r *render.Renderer
|
||||
|
||||
db roomdb.AliasService
|
||||
|
||||
muxrpcHostAndPort string
|
||||
roomID refs.FeedRef
|
||||
}
|
||||
|
||||
func (a aliasHandler) resolve(rw http.ResponseWriter, req *http.Request) {
|
||||
respEncoding := req.URL.Query().Get("encoding")
|
||||
|
||||
var ar aliasResponder
|
||||
switch respEncoding {
|
||||
case "json":
|
||||
ar = newAliasJSONResponder(rw)
|
||||
default:
|
||||
ar = newAliasHTMLResponder(a.r, rw, req)
|
||||
}
|
||||
|
||||
ar.UpdateRoomInfo(a.muxrpcHostAndPort, a.roomID)
|
||||
|
||||
name := mux.Vars(req)["alias"]
|
||||
if name == "" && !aliases.IsValid(name) {
|
||||
ar.SendError(fmt.Errorf("invalid alias"))
|
||||
return
|
||||
}
|
||||
|
||||
alias, err := a.db.Resolve(req.Context(), name)
|
||||
if err != nil {
|
||||
ar.SendError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ar.SendConfirmation(alias)
|
||||
}
|
||||
|
||||
// aliasResponder is supposed handle different encoding types transparently.
|
||||
// It either sends the signed alias confirmation or an error.
|
||||
type aliasResponder interface {
|
||||
SendConfirmation(roomdb.Alias)
|
||||
SendError(error)
|
||||
|
||||
UpdateRoomInfo(hostAndPort string, roomID refs.FeedRef)
|
||||
}
|
||||
|
||||
// aliasJSONResponse dictates the field names and format of the JSON response for the alias web endpoint
|
||||
type aliasJSONResponse struct {
|
||||
Status string `json:"status"`
|
||||
Address string `json:"address"`
|
||||
RoomID string `json:"roomId"`
|
||||
UserID string `json:"userId"`
|
||||
Alias string `json:"alias"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// handles JSON responses
|
||||
type aliasJSONResponder struct {
|
||||
enc *json.Encoder
|
||||
|
||||
roomID refs.FeedRef
|
||||
multiservAddr string
|
||||
}
|
||||
|
||||
func newAliasJSONResponder(rw http.ResponseWriter) aliasResponder {
|
||||
return &aliasJSONResponder{
|
||||
enc: json.NewEncoder(rw),
|
||||
}
|
||||
}
|
||||
|
||||
func (json *aliasJSONResponder) UpdateRoomInfo(hostAndPort string, roomID refs.FeedRef) {
|
||||
json.roomID = roomID
|
||||
|
||||
roomPubKey := base64.StdEncoding.EncodeToString(roomID.PubKey())
|
||||
json.multiservAddr = fmt.Sprintf("net:%s~shs:%s", hostAndPort, roomPubKey)
|
||||
}
|
||||
|
||||
func (json aliasJSONResponder) SendConfirmation(alias roomdb.Alias) {
|
||||
var resp = aliasJSONResponse{
|
||||
Status: "successfull",
|
||||
RoomID: json.roomID.Ref(),
|
||||
Alias: alias.Name,
|
||||
UserID: alias.Feed.Ref(),
|
||||
Signature: base64.StdEncoding.EncodeToString(alias.Signature),
|
||||
}
|
||||
json.enc.Encode(resp)
|
||||
}
|
||||
|
||||
func (json aliasJSONResponder) SendError(err error) {
|
||||
json.enc.Encode(struct {
|
||||
Status string `json:"status"`
|
||||
Error string `json:"error"`
|
||||
}{"error", err.Error()})
|
||||
}
|
||||
|
||||
// handles HTML responses
|
||||
type aliasHTMLResponder struct {
|
||||
renderer *render.Renderer
|
||||
rw http.ResponseWriter
|
||||
req *http.Request
|
||||
|
||||
roomID refs.FeedRef
|
||||
multiservAddr string
|
||||
}
|
||||
|
||||
func newAliasHTMLResponder(r *render.Renderer, rw http.ResponseWriter, req *http.Request) aliasResponder {
|
||||
return &aliasHTMLResponder{
|
||||
renderer: r,
|
||||
rw: rw,
|
||||
req: req,
|
||||
}
|
||||
}
|
||||
|
||||
func (html *aliasHTMLResponder) UpdateRoomInfo(hostAndPort string, roomID refs.FeedRef) {
|
||||
html.roomID = roomID
|
||||
|
||||
roomPubKey := base64.StdEncoding.EncodeToString(roomID.PubKey())
|
||||
html.multiservAddr = fmt.Sprintf("net:%s~shs:%s", hostAndPort, roomPubKey)
|
||||
}
|
||||
|
||||
func (html aliasHTMLResponder) SendConfirmation(alias roomdb.Alias) {
|
||||
err := html.renderer.Render(html.rw, html.req, "aliases-resolved.html", http.StatusOK, struct {
|
||||
Alias roomdb.Alias
|
||||
}{alias})
|
||||
if err != nil {
|
||||
log.Println("alias-resolve render errr:", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (html aliasHTMLResponder) SendError(err error) {
|
||||
html.renderer.Error(html.rw, html.req, http.StatusInternalServerError, err)
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
)
|
||||
|
||||
func TestAliasResolve(t *testing.T) {
|
||||
ts := setup(t)
|
||||
|
||||
a := assert.New(t)
|
||||
r := require.New(t)
|
||||
|
||||
var testAlias = roomdb.Alias{
|
||||
ID: 54321,
|
||||
Name: "test-name",
|
||||
Feed: refs.FeedRef{
|
||||
ID: bytes.Repeat([]byte{'F'}, 32),
|
||||
Algo: "test",
|
||||
},
|
||||
Signature: bytes.Repeat([]byte{'S'}, 32),
|
||||
}
|
||||
ts.AliasesDB.ResolveReturns(testAlias, nil)
|
||||
|
||||
// default is HTML
|
||||
htmlURL, err := ts.Router.Get(router.CompleteAliasResolve).URL("alias", testAlias.Name)
|
||||
r.Nil(err)
|
||||
t.Log("resolving", htmlURL.String())
|
||||
html, resp := ts.Client.GetHTML(htmlURL.String())
|
||||
a.Equal(http.StatusOK, resp.Code)
|
||||
|
||||
a.Equal(testAlias.Name, html.Find("title").Text())
|
||||
|
||||
// default is HTML
|
||||
jsonURL, err := ts.Router.Get(router.CompleteAliasResolve).URL("alias", testAlias.Name)
|
||||
r.Nil(err)
|
||||
q := jsonURL.Query()
|
||||
q.Set("encoding", "json")
|
||||
jsonURL.RawQuery = q.Encode()
|
||||
t.Log("resolving", jsonURL.String())
|
||||
resp = ts.Client.GetBody(jsonURL.String())
|
||||
a.Equal(http.StatusOK, resp.Code)
|
||||
|
||||
var ar aliasJSONResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&ar)
|
||||
r.NoError(err)
|
||||
a.Equal(testAlias.Name, ar.Alias)
|
||||
sigData, err := base64.StdEncoding.DecodeString(ar.Signature)
|
||||
r.NoError(err)
|
||||
a.Equal(testAlias.Signature, sigData)
|
||||
a.Equal(testAlias.Feed.Ref(), ar.UserID, "wrong user feed on response")
|
||||
a.Equal(ts.NetworkInfo.RoomID.Ref(), ar.RoomID, "wrong room feed on response")
|
||||
}
|
|
@ -26,7 +26,6 @@ func TestIndex(t *testing.T) {
|
|||
webassert.Localized(t, html, []webassert.LocalizedElement{
|
||||
{"h1", "Default Notice Title"},
|
||||
{"title", "Default Notice Title"},
|
||||
// {"#nav", "FooBar"},
|
||||
})
|
||||
|
||||
content := html.Find("p").Text()
|
||||
|
|
|
@ -11,13 +11,14 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"go.mindeco.de/http/auth"
|
||||
"go.mindeco.de/http/render"
|
||||
"go.mindeco.de/logging"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/internal/repo"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
|
@ -33,6 +34,7 @@ import (
|
|||
var HTMLTemplates = []string{
|
||||
"landing/index.tmpl",
|
||||
"landing/about.tmpl",
|
||||
"aliases-resolved.html",
|
||||
"invite/accept.tmpl",
|
||||
"invite/consumed.tmpl",
|
||||
"notice/list.tmpl",
|
||||
|
@ -56,7 +58,7 @@ type NetworkInfo struct {
|
|||
PortMUXRPC uint
|
||||
PortHTTPS uint // 0 assumes default (443)
|
||||
|
||||
PubKey ed25519.PublicKey
|
||||
RoomID refs.FeedRef
|
||||
|
||||
Domain string
|
||||
}
|
||||
|
@ -262,10 +264,20 @@ func New(
|
|||
m.Get(router.CompleteNoticeList).Handler(r.HTML("notice/list.tmpl", nh.list))
|
||||
m.Get(router.CompleteNoticeShow).Handler(r.HTML("notice/show.tmpl", nh.show))
|
||||
|
||||
var ah = aliasHandler{
|
||||
r: r,
|
||||
|
||||
db: dbs.Aliases,
|
||||
|
||||
roomID: netInfo.RoomID,
|
||||
muxrpcHostAndPort: fmt.Sprintf("%s:%d", netInfo.Domain, netInfo.PortMUXRPC),
|
||||
}
|
||||
m.Get(router.CompleteAliasResolve).HandlerFunc(ah.resolve)
|
||||
|
||||
var ih = inviteHandler{
|
||||
invites: dbs.Invites,
|
||||
|
||||
roomPubKey: netInfo.PubKey,
|
||||
roomPubKey: netInfo.RoomID.PubKey(),
|
||||
muxrpcHostAndPort: fmt.Sprintf("%s:%d", netInfo.Domain, netInfo.PortMUXRPC),
|
||||
}
|
||||
m.Get(router.CompleteInviteAccept).Handler(r.HTML("invite/accept.tmpl", ih.acceptForm))
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"go.mindeco.de/http/render"
|
||||
"go.mindeco.de/logging"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
|
@ -18,8 +17,6 @@ import (
|
|||
)
|
||||
|
||||
type inviteHandler struct {
|
||||
r *render.Renderer
|
||||
|
||||
invites roomdb.InviteService
|
||||
aliases roomdb.AliasService
|
||||
|
||||
|
|
|
@ -226,7 +226,7 @@ func TestInviteConsumeInvite(t *testing.T) {
|
|||
|
||||
// TODO: this is just a cheap stub for actual ssb-uri parsing
|
||||
a.True(strings.HasPrefix(gotRA, "net:localhost:8008~shs:"), "not for the test host: %s", gotRA)
|
||||
a.True(strings.Contains(gotRA, base64.StdEncoding.EncodeToString(ts.NetworkInfo.PubKey)), "public key missing? %s", gotRA)
|
||||
a.True(strings.Contains(gotRA, base64.StdEncoding.EncodeToString(ts.NetworkInfo.RoomID.PubKey())), "public key missing? %s", gotRA)
|
||||
a.True(strings.HasSuffix(gotRA, ":SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24="), "magic suffix missing: %s", gotRA)
|
||||
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"go.mindeco.de/http/tester"
|
||||
"go.mindeco.de/logging/logtest"
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/internal/repo"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb/mockdb"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/internal/repo"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/roomstate"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/web/i18n"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
|
||||
|
@ -33,6 +34,7 @@ type testSession struct {
|
|||
// mocked dbs
|
||||
AuthDB *mockdb.FakeAuthWithSSBService
|
||||
AuthFallbackDB *mockdb.FakeAuthFallbackService
|
||||
AliasesDB *mockdb.FakeAliasService
|
||||
AllowListDB *mockdb.FakeAllowListService
|
||||
InvitesDB *mockdb.FakeInviteService
|
||||
PinnedDB *mockdb.FakePinnedNoticesService
|
||||
|
@ -61,6 +63,7 @@ func setup(t *testing.T) *testSession {
|
|||
|
||||
ts.AuthDB = new(mockdb.FakeAuthWithSSBService)
|
||||
ts.AuthFallbackDB = new(mockdb.FakeAuthFallbackService)
|
||||
ts.AliasesDB = new(mockdb.FakeAliasService)
|
||||
ts.AllowListDB = new(mockdb.FakeAllowListService)
|
||||
ts.InvitesDB = new(mockdb.FakeInviteService)
|
||||
ts.PinnedDB = new(mockdb.FakePinnedNoticesService)
|
||||
|
@ -76,7 +79,10 @@ func setup(t *testing.T) *testSession {
|
|||
PortMUXRPC: 8008,
|
||||
PortHTTPS: 443,
|
||||
|
||||
PubKey: bytes.Repeat([]byte("test"), 8),
|
||||
RoomID: refs.FeedRef{
|
||||
ID: bytes.Repeat([]byte("test"), 8),
|
||||
Algo: refs.RefAlgoFeedSSB1,
|
||||
},
|
||||
}
|
||||
|
||||
log, _ := logtest.KitLogger("complete", t)
|
||||
|
@ -91,6 +97,7 @@ func setup(t *testing.T) *testSession {
|
|||
ts.NetworkInfo,
|
||||
ts.RoomState,
|
||||
Databases{
|
||||
Aliases: ts.AliasesDB,
|
||||
AuthWithSSB: ts.AuthDB,
|
||||
AuthFallback: ts.AuthFallbackDB,
|
||||
AllowList: ts.AllowListDB,
|
||||
|
|
|
@ -14,6 +14,8 @@ const (
|
|||
CompleteNoticeShow = "complete:notice:show"
|
||||
CompleteNoticeList = "complete:notice:list"
|
||||
|
||||
CompleteAliasResolve = "complete:alias:resolve"
|
||||
|
||||
CompleteInviteAccept = "complete:invite:accept"
|
||||
CompleteInviteConsume = "complete:invite:consume"
|
||||
)
|
||||
|
@ -28,6 +30,8 @@ func CompleteApp() *mux.Router {
|
|||
m.Path("/").Methods("GET").Name(CompleteIndex)
|
||||
m.Path("/about").Methods("GET").Name(CompleteAbout)
|
||||
|
||||
m.Path("/{alias}").Methods("GET").Name(CompleteAliasResolve)
|
||||
|
||||
m.Path("/invite/accept").Methods("GET").Name(CompleteInviteAccept)
|
||||
m.Path("/invite/consume").Methods("POST").Name(CompleteInviteConsume)
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{{ define "title" }}{{.Alias.Name}}{{ end }}
|
||||
{{ define "content" }}
|
||||
<div>
|
||||
<h1>{{.Alias.Name}}</h1>
|
||||
</div>
|
||||
{{end}}
|
Loading…
Reference in New Issue