2021-03-04 14:09:14 +00:00
|
|
|
package handlers
|
2021-03-08 14:47:30 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-03-09 17:27:44 +00:00
|
|
|
"encoding/base64"
|
2021-03-26 19:08:13 +00:00
|
|
|
"encoding/json"
|
2021-03-08 14:47:30 +00:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2021-03-09 17:27:44 +00:00
|
|
|
"strings"
|
2021-03-08 14:47:30 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2021-03-10 15:44:46 +00:00
|
|
|
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
|
2021-03-08 14:47:30 +00:00
|
|
|
weberrors "github.com/ssb-ngi-pointer/go-ssb-room/web/errors"
|
|
|
|
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
|
2021-03-11 08:55:38 +00:00
|
|
|
"github.com/ssb-ngi-pointer/go-ssb-room/web/webassert"
|
2021-03-26 19:08:13 +00:00
|
|
|
refs "go.mindeco.de/ssb-refs"
|
2021-03-08 14:47:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestInviteShowAcceptForm(t *testing.T) {
|
|
|
|
ts := setup(t)
|
|
|
|
|
|
|
|
t.Run("token doesnt exist", func(t *testing.T) {
|
|
|
|
a, r := assert.New(t), require.New(t)
|
|
|
|
|
|
|
|
testToken := "nonexistant-test-token"
|
2021-04-01 07:04:38 +00:00
|
|
|
acceptURL404 := ts.URLTo(router.CompleteInviteFacade, "token", testToken)
|
2021-03-08 14:47:30 +00:00
|
|
|
r.NotNil(acceptURL404)
|
|
|
|
|
|
|
|
// prep the mocked db for http:404
|
2021-03-10 15:44:46 +00:00
|
|
|
ts.InvitesDB.GetByTokenReturns(roomdb.Invite{}, roomdb.ErrNotFound)
|
2021-03-08 14:47:30 +00:00
|
|
|
|
|
|
|
// request the form
|
2021-04-01 07:04:38 +00:00
|
|
|
doc, resp := ts.Client.GetHTML(acceptURL404)
|
2021-03-08 14:47:30 +00:00
|
|
|
// 500 until https://github.com/ssb-ngi-pointer/go-ssb-room/issues/66 is fixed
|
|
|
|
a.Equal(http.StatusInternalServerError, resp.Code)
|
|
|
|
|
|
|
|
// check database calls
|
|
|
|
r.EqualValues(1, ts.InvitesDB.GetByTokenCallCount())
|
|
|
|
_, tokenFromArg := ts.InvitesDB.GetByTokenArgsForCall(0)
|
|
|
|
a.Equal(testToken, tokenFromArg)
|
|
|
|
|
|
|
|
// fix #66
|
|
|
|
// assertLocalized(t, doc, []localizedElement{
|
|
|
|
// {"#welcome", "AuthFallbackWelcome"},
|
|
|
|
// {"title", "AuthFallbackTitle"},
|
|
|
|
// })
|
|
|
|
gotErr := doc.Find("#errBody").Text()
|
|
|
|
wantErr := weberrors.ErrNotFound{What: "invite"}
|
|
|
|
a.EqualError(wantErr, gotErr)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("token DOES exist", func(t *testing.T) {
|
|
|
|
a, r := assert.New(t), require.New(t)
|
|
|
|
|
|
|
|
testToken := "existing-test-token"
|
2021-04-01 07:04:38 +00:00
|
|
|
validAcceptURL := ts.URLTo(router.CompleteInviteFacade, "token", testToken)
|
2021-03-08 14:47:30 +00:00
|
|
|
|
|
|
|
// prep the mocked db for http:200
|
2021-03-29 08:23:03 +00:00
|
|
|
fakeExistingInvite := roomdb.Invite{ID: 1234}
|
2021-03-08 14:47:30 +00:00
|
|
|
ts.InvitesDB.GetByTokenReturns(fakeExistingInvite, nil)
|
|
|
|
|
|
|
|
// request the form
|
2021-04-01 07:04:38 +00:00
|
|
|
doc, resp := ts.Client.GetHTML(validAcceptURL)
|
2021-03-08 14:47:30 +00:00
|
|
|
a.Equal(http.StatusOK, resp.Code)
|
|
|
|
|
|
|
|
// check database calls
|
|
|
|
r.EqualValues(2, ts.InvitesDB.GetByTokenCallCount())
|
|
|
|
_, tokenFromArg := ts.InvitesDB.GetByTokenArgsForCall(1)
|
|
|
|
a.Equal(testToken, tokenFromArg)
|
|
|
|
|
2021-03-11 08:55:38 +00:00
|
|
|
webassert.Localized(t, doc, []webassert.LocalizedElement{
|
2021-04-06 09:32:29 +00:00
|
|
|
{"#join-room-uri", "InviteFacadeJoin"},
|
2021-03-26 19:08:13 +00:00
|
|
|
{"title", "InviteFacadeTitle"},
|
2021-03-08 14:47:30 +00:00
|
|
|
})
|
|
|
|
|
2021-04-06 09:32:29 +00:00
|
|
|
// Empty href
|
|
|
|
joinHref, ok := doc.Find("#join-room-uri").Attr("href")
|
|
|
|
a.Equal("#", joinHref)
|
|
|
|
a.True(ok)
|
|
|
|
|
|
|
|
// Fallback URL in data-href-fallback
|
2021-04-01 07:04:38 +00:00
|
|
|
fallbackURL := ts.URLTo(router.CompleteInviteFacadeFallback, "token", testToken)
|
|
|
|
want := fallbackURL.Path + "?" + fallbackURL.RawQuery
|
2021-04-06 09:32:29 +00:00
|
|
|
joinDataHrefFallback, ok := doc.Find("#join-room-uri").Attr("data-href-fallback")
|
2021-04-01 07:04:38 +00:00
|
|
|
a.Equal(want, joinDataHrefFallback)
|
2021-04-06 09:32:29 +00:00
|
|
|
a.True(ok)
|
|
|
|
|
|
|
|
// ssb-uri in data-href
|
|
|
|
joinDataHref, ok := doc.Find("#join-room-uri").Attr("data-href")
|
|
|
|
a.True(ok)
|
|
|
|
joinURI, err := url.Parse(joinDataHref)
|
|
|
|
r.NoError(err)
|
|
|
|
|
|
|
|
a.Equal("ssb", joinURI.Scheme)
|
|
|
|
a.Equal("experimental", joinURI.Opaque)
|
|
|
|
|
|
|
|
params := joinURI.Query()
|
|
|
|
a.Equal("join-room", params.Get("action"))
|
|
|
|
|
|
|
|
inviteParam := params.Get("invite")
|
|
|
|
a.Equal(testToken, inviteParam)
|
|
|
|
|
|
|
|
postTo := params.Get("postTo")
|
2021-04-01 07:04:38 +00:00
|
|
|
expectedConsumeInviteURL := ts.URLTo(router.CompleteInviteConsume)
|
2021-04-06 09:32:29 +00:00
|
|
|
a.Equal(expectedConsumeInviteURL.String(), postTo)
|
2021-03-08 14:47:30 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-03-26 19:08:13 +00:00
|
|
|
func TestInviteConsumeInviteHTTP(t *testing.T) {
|
2021-03-08 14:47:30 +00:00
|
|
|
ts := setup(t)
|
|
|
|
a, r := assert.New(t), require.New(t)
|
|
|
|
|
|
|
|
testToken := "existing-test-token-2"
|
2021-04-01 07:04:38 +00:00
|
|
|
validAcceptURL := ts.URLTo(router.CompleteInviteInsertID, "token", testToken)
|
2021-03-26 19:08:13 +00:00
|
|
|
testInvite := roomdb.Invite{ID: 4321}
|
|
|
|
ts.InvitesDB.GetByTokenReturns(testInvite, nil)
|
2021-03-08 14:47:30 +00:00
|
|
|
|
|
|
|
// request the form (for a valid csrf token)
|
2021-04-01 07:04:38 +00:00
|
|
|
doc, resp := ts.Client.GetHTML(validAcceptURL)
|
2021-03-08 14:47:30 +00:00
|
|
|
a.Equal(http.StatusOK, resp.Code)
|
|
|
|
|
2021-04-06 09:32:29 +00:00
|
|
|
form := doc.Find("form#consume")
|
|
|
|
r.Equal(1, form.Length())
|
|
|
|
|
2021-04-01 07:04:38 +00:00
|
|
|
consumeInviteURLString, has := form.Attr("action")
|
|
|
|
a.True(has, "form should have an action attribute")
|
|
|
|
expectedConsumeInviteURL := ts.URLTo(router.CompleteInviteConsume)
|
|
|
|
a.Equal(expectedConsumeInviteURL.Path, consumeInviteURLString)
|
|
|
|
|
2021-04-06 09:32:29 +00:00
|
|
|
webassert.CSRFTokenPresent(t, form)
|
|
|
|
webassert.ElementsInForm(t, form, []webassert.FormElement{
|
|
|
|
{Name: "invite", Type: "hidden", Value: testToken},
|
|
|
|
{Name: "id", Type: "text"},
|
|
|
|
})
|
|
|
|
|
2021-03-08 14:47:30 +00:00
|
|
|
// get the corresponding token from the page
|
|
|
|
csrfTokenElem := doc.Find("input[name='gorilla.csrf.Token']")
|
|
|
|
a.Equal(1, csrfTokenElem.Length())
|
|
|
|
csrfName, has := csrfTokenElem.Attr("name")
|
|
|
|
a.True(has, "should have a name attribute")
|
|
|
|
csrfValue, has := csrfTokenElem.Attr("value")
|
|
|
|
a.True(has, "should have value attribute")
|
|
|
|
|
|
|
|
// create the consume request
|
|
|
|
testNewMember := refs.FeedRef{
|
|
|
|
ID: bytes.Repeat([]byte{1}, 32),
|
|
|
|
Algo: refs.RefAlgoFeedSSB1,
|
|
|
|
}
|
|
|
|
consumeVals := url.Values{
|
2021-03-26 19:08:13 +00:00
|
|
|
"invite": []string{testToken},
|
|
|
|
"id": []string{testNewMember.Ref()},
|
2021-03-08 14:47:30 +00:00
|
|
|
|
|
|
|
csrfName: []string{csrfValue},
|
|
|
|
}
|
|
|
|
|
|
|
|
// construct the consume endpoint url
|
2021-04-01 07:04:38 +00:00
|
|
|
consumeInviteURL := ts.URLTo(router.CompleteInviteConsume)
|
|
|
|
|
|
|
|
// construct the header with the Referer or csrf check
|
2021-03-08 14:47:30 +00:00
|
|
|
var csrfCookieHeader = http.Header(map[string][]string{})
|
|
|
|
csrfCookieHeader.Set("Referer", "https://localhost")
|
|
|
|
ts.Client.SetHeaders(csrfCookieHeader)
|
|
|
|
|
|
|
|
// prepare the mock
|
2021-03-26 19:08:13 +00:00
|
|
|
ts.InvitesDB.ConsumeReturns(testInvite, nil)
|
2021-03-08 14:47:30 +00:00
|
|
|
|
|
|
|
// send the POST
|
2021-04-01 07:04:38 +00:00
|
|
|
resp = ts.Client.PostForm(consumeInviteURL, consumeVals)
|
2021-03-08 14:47:30 +00:00
|
|
|
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code for sign in")
|
|
|
|
|
|
|
|
// check how consume was called
|
|
|
|
r.EqualValues(1, ts.InvitesDB.ConsumeCallCount())
|
|
|
|
_, tokenFromArg, newMemberRef := ts.InvitesDB.ConsumeArgsForCall(0)
|
|
|
|
a.Equal(testToken, tokenFromArg)
|
|
|
|
a.True(newMemberRef.Equal(&testNewMember))
|
2021-03-26 19:08:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestInviteConsumeInviteJSON(t *testing.T) {
|
|
|
|
ts := setup(t)
|
|
|
|
a, r := assert.New(t), require.New(t)
|
|
|
|
|
|
|
|
testToken := "existing-test-token-2"
|
2021-03-09 17:27:44 +00:00
|
|
|
|
2021-03-26 19:08:13 +00:00
|
|
|
testInvite := roomdb.Invite{ID: 4321}
|
|
|
|
ts.InvitesDB.GetByTokenReturns(testInvite, nil)
|
|
|
|
|
|
|
|
// create the consume request
|
|
|
|
testNewMember := refs.FeedRef{
|
|
|
|
ID: bytes.Repeat([]byte{1}, 32),
|
|
|
|
Algo: refs.RefAlgoFeedSSB1,
|
|
|
|
}
|
|
|
|
|
|
|
|
var consume inviteConsumePayload
|
|
|
|
consume.Invite = testToken
|
|
|
|
consume.ID = testNewMember
|
|
|
|
|
|
|
|
// construct the consume endpoint url
|
2021-04-01 07:04:38 +00:00
|
|
|
consumeInviteURL := ts.URLTo(router.CompleteInviteConsume)
|
2021-03-29 08:23:03 +00:00
|
|
|
r.NotNil(consumeInviteURL)
|
2021-03-26 19:08:13 +00:00
|
|
|
|
|
|
|
// prepare the mock
|
|
|
|
ts.InvitesDB.ConsumeReturns(testInvite, nil)
|
|
|
|
|
|
|
|
// send the POST
|
2021-04-01 07:04:38 +00:00
|
|
|
resp := ts.Client.SendJSON(consumeInviteURL, consume)
|
2021-03-26 19:08:13 +00:00
|
|
|
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code for sign in")
|
|
|
|
|
|
|
|
// check how consume was called
|
|
|
|
r.EqualValues(1, ts.InvitesDB.ConsumeCallCount())
|
|
|
|
_, tokenFromArg, newMemberRef := ts.InvitesDB.ConsumeArgsForCall(0)
|
|
|
|
a.Equal(testToken, tokenFromArg)
|
|
|
|
a.True(newMemberRef.Equal(&testNewMember))
|
|
|
|
|
|
|
|
var jsonConsumeResp inviteConsumeJSONResponse
|
2021-03-29 08:23:03 +00:00
|
|
|
err := json.NewDecoder(resp.Body).Decode(&jsonConsumeResp)
|
2021-03-26 19:08:13 +00:00
|
|
|
r.NoError(err)
|
|
|
|
|
|
|
|
a.Equal("successful", jsonConsumeResp.Status)
|
|
|
|
|
|
|
|
gotRA := jsonConsumeResp.RoomAddress
|
|
|
|
a.True(strings.HasPrefix(gotRA, "net:localhost:8008~shs:"), "not for the test host: %s", gotRA)
|
|
|
|
a.True(strings.HasSuffix(gotRA, base64.StdEncoding.EncodeToString(ts.NetworkInfo.RoomID.PubKey())), "public key missing? %s", gotRA)
|
2021-03-08 14:47:30 +00:00
|
|
|
}
|
2021-04-07 10:57:16 +00:00
|
|
|
|
|
|
|
func TestInviteConsumptionDenied(t *testing.T) {
|
|
|
|
ts := setup(t)
|
|
|
|
a, r := assert.New(t), require.New(t)
|
|
|
|
urlTo := web.NewURLTo(ts.Router)
|
|
|
|
|
|
|
|
testToken := "existing-test-token-2"
|
|
|
|
validAcceptURL := urlTo(router.CompleteInviteFacade, "token", testToken)
|
|
|
|
r.NotNil(validAcceptURL)
|
|
|
|
|
|
|
|
testInvite := roomdb.Invite{ID: 4321}
|
|
|
|
ts.InvitesDB.GetByTokenReturns(testInvite, nil)
|
|
|
|
|
|
|
|
ts.DeniedKeysDB.HasFeedReturns(true)
|
|
|
|
|
|
|
|
// create the consume request
|
|
|
|
testNewMember := refs.FeedRef{
|
|
|
|
ID: bytes.Repeat([]byte{1}, 32),
|
|
|
|
Algo: refs.RefAlgoFeedSSB1,
|
|
|
|
}
|
|
|
|
|
|
|
|
var consume inviteConsumePayload
|
|
|
|
consume.Invite = testToken
|
|
|
|
consume.ID = testNewMember
|
|
|
|
|
|
|
|
// construct the consume endpoint url
|
|
|
|
consumeInviteURL := urlTo(router.CompleteInviteConsume)
|
|
|
|
r.NotNil(consumeInviteURL)
|
|
|
|
|
|
|
|
// prepare the mock
|
|
|
|
ts.InvitesDB.ConsumeReturns(testInvite, nil)
|
|
|
|
|
|
|
|
// send the POST
|
|
|
|
resp := ts.Client.SendJSON(consumeInviteURL.String(), consume)
|
|
|
|
|
|
|
|
// decode the json response
|
|
|
|
var jsonConsumeResp inviteConsumeJSONResponse
|
|
|
|
err := json.NewDecoder(resp.Body).Decode(&jsonConsumeResp)
|
|
|
|
r.NoError(err)
|
|
|
|
|
|
|
|
// json response should indicate an error for the denied key
|
|
|
|
a.Equal("error", jsonConsumeResp.Status)
|
|
|
|
|
|
|
|
// invite should not be consumed
|
2021-04-07 11:03:14 +00:00
|
|
|
r.EqualValues(0, ts.InvitesDB.ConsumeCallCount())
|
2021-04-07 10:57:16 +00:00
|
|
|
}
|