Merge pull request #200 from ssb-ngi-pointer/privacy-mode-tests

Privacy mode tests 💯 💯
This commit is contained in:
Alexander Cobleigh 2021-04-26 09:34:55 +02:00 committed by GitHub
commit fa6fa3267e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 226 additions and 23 deletions

View File

@ -84,3 +84,141 @@ func TestConnEstablishmentDeniedKey(t *testing.T) {
cancel()
}
func TestConnEstablishmentDenyNonMembersRestrictedRoom(t *testing.T) {
// defer leakcheck.Check(t)
ctx, cancel := context.WithCancel(context.Background())
theBots := createServerAndBots(t, ctx, 2)
r := require.New(t)
a := assert.New(t)
const (
indexSrv = iota
indexA
indexB
)
serv := theBots[indexSrv].srv
botA := theBots[indexA].srv
botB := theBots[indexB].srv
// make sure we are running in restricted mode on the server
err := serv.Config.SetPrivacyMode(ctx, roomdb.ModeRestricted)
r.NoError(err)
// allow A, deny B
theBots[indexSrv].srv.Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
// since we want to verify denying connections for non members in a restricted room, let us:
// a) NOT add B as a member
// theBots[indexSrv].srv.Members.Add(ctx, botB.Whoami(), roomdb.RoleMember)
// hack: allow bots to dial the server
theBots[indexA].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
// dial up B->A and C->A
// should work (we allowed A)
err = botA.Network.Connect(ctx, serv.Network.GetListenAddr())
r.NoError(err, "connect A to the Server")
// shouldn't work (we disallow B in restricted mode)
err = botB.Network.Connect(ctx, serv.Network.GetListenAddr())
r.NoError(err, "connect B to the Server") // we dont see an error because it just establishes the tcp connection
t.Log("letting handshaking settle..")
time.Sleep(1 * time.Second)
var srvWho struct {
ID refs.FeedRef
}
endpointB, has := botB.Network.GetEndpointFor(serv.Whoami())
r.False(has, "botB has an endpoint for the server!")
if endpointB != nil {
a.Nil(endpointB, "should not have an endpoint on B (B is not a member, and the server is restricted)")
err = endpointB.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"})
r.Error(err)
t.Log(srvWho.ID.Ref())
}
endpointA, has := botA.Network.GetEndpointFor(serv.Whoami())
r.True(has, "botA has no endpoint for the server")
err = endpointA.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"})
r.NoError(err)
t.Log("server whoami:", srvWho.ID.Ref())
a.True(serv.Whoami().Equal(&srvWho.ID))
cancel()
}
func TestConnEstablishmentAllowNonMembersCommunityRoom(t *testing.T) {
// defer leakcheck.Check(t)
ctx, cancel := context.WithCancel(context.Background())
theBots := createServerAndBots(t, ctx, 2)
r := require.New(t)
a := assert.New(t)
const (
indexSrv = iota
indexA
indexB
)
serv := theBots[indexSrv].srv
botA := theBots[indexA].srv
botB := theBots[indexB].srv
// make sure we are running in community mode on the server
err := serv.Config.SetPrivacyMode(ctx, roomdb.ModeCommunity)
r.NoError(err)
pm, err := serv.Config.GetPrivacyMode(ctx)
r.NoError(err)
r.EqualValues(roomdb.ModeCommunity, pm)
// allow A, allow B
theBots[indexSrv].srv.Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
// since we want to verify allowing connections for non members in a community room, let us:
// a) NOT add B as a member
// theBots[indexSrv].srv.Members.Add(ctx, botB.Whoami(), roomdb.RoleMember)
// hack: allow bots to dial the server
theBots[indexA].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
// dial up B->A and C->A
// should work (we allowed A)
err = botA.Network.Connect(ctx, serv.Network.GetListenAddr())
r.NoError(err, "connect A to the Server")
// should work (we don't disallow B in community mode)
err = botB.Network.Connect(ctx, serv.Network.GetListenAddr())
r.NoError(err, "connect B to the Server")
t.Log("letting handshaking settle..")
time.Sleep(1 * time.Second)
var srvWho struct {
ID refs.FeedRef
}
endpointB, has := botB.Network.GetEndpointFor(serv.Whoami())
r.True(has, "botB has no endpoint for the server")
err = endpointB.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"})
r.NoError(err)
t.Log(srvWho.ID.Ref())
endpointA, has := botA.Network.GetEndpointFor(serv.Whoami())
r.True(has, "botA has no endpoint for the server")
err = endpointA.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"})
r.NoError(err)
t.Log("server whoami:", srvWho.ID.Ref())
a.True(serv.Whoami().Equal(&srvWho.ID))
cancel()
}

View File

@ -35,7 +35,7 @@ func (s *Server) initNetwork() error {
// if privacy mode is restricted, deny connections from non-members
if pm == roomdb.ModeRestricted {
if _, err := s.authorizer.GetByFeed(s.rootCtx, *remote); err != nil {
if _, err := s.Members.GetByFeed(s.rootCtx, *remote); err != nil {
return nil, fmt.Errorf("access restricted to members")
}
}

View File

@ -61,8 +61,6 @@ type Server struct {
public typemux.HandlerMux
master typemux.HandlerMux
authorizer roomdb.MembersService
StateManager *roomstate.Manager
Members roomdb.MembersService
@ -89,7 +87,6 @@ func New(
opts ...Option,
) (*Server, error) {
var s Server
s.authorizer = membersdb
s.Members = membersdb
s.DeniedKeys = deniedkeysdb

View File

@ -34,7 +34,7 @@ func (eh *ErrorHandler) SetRenderer(r *render.Renderer) {
func (eh *ErrorHandler) Handle(rw http.ResponseWriter, req *http.Request, code int, err error) {
log := logging.FromContext(req.Context())
level.Error(log).Log("event", "handling error","path", req.URL.Path, "err",err)
level.Error(log).Log("event", "handling error", "path", req.URL.Path, "err", err)
var redirectErr ErrRedirect
if errors.As(err, &redirectErr) {
if redirectErr.Reason != nil {

View File

@ -53,7 +53,13 @@ func TestAliasesRevoke(t *testing.T) {
urlRevoke := ts.URLTo(router.AdminAliasesRevoke)
overviewURL := ts.URLTo(router.AdminMembersOverview)
aliasEntry := roomdb.Alias{
ID: ts.User.ID,
Feed: ts.User.PubKey,
Name: "Blobby",
}
ts.AliasesDB.RevokeReturns(nil)
ts.AliasesDB.ResolveReturns(aliasEntry, nil)
addVals := url.Values{"name": []string{"the-name"}}
rec := ts.Client.PostForm(urlRevoke, addVals)

View File

@ -76,11 +76,11 @@ func (h invitesHandler) create(w http.ResponseWriter, req *http.Request) (interf
case roomdb.ModeOpen:
case roomdb.ModeCommunity:
if member.Role == roomdb.RoleUnknown {
return nil, fmt.Errorf("warning: member with unknown role tried to create an invite")
return nil, weberrors.ErrNotAuthorized
}
case roomdb.ModeRestricted:
if member.Role == roomdb.RoleMember || member.Role == roomdb.RoleUnknown {
return nil, fmt.Errorf("warning: non-admin/mod user tried to create an invite")
return nil, weberrors.ErrNotAuthorized
}
}

View File

@ -2,6 +2,7 @@ package admin
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
@ -143,17 +144,28 @@ func TestInvitesCreate(t *testing.T) {
a := assert.New(t)
r := require.New(t)
urlRemove := ts.URLTo(router.AdminInvitesCreate)
urlCreate := ts.URLTo(router.AdminInvitesCreate)
testInvite := "your-fake-test-invite"
ts.InvitesDB.CreateReturns(testInvite, nil)
rec := ts.Client.PostForm(urlRemove, url.Values{})
a.Equal(http.StatusOK, rec.Code)
totalCreateCallCount := 0
createInviteShouldWork := func(works bool) *httptest.ResponseRecorder {
rec := ts.Client.PostForm(urlCreate, url.Values{})
if works {
totalCreateCallCount += 1
a.Equal(http.StatusOK, rec.Code)
r.Equal(totalCreateCallCount, ts.InvitesDB.CreateCallCount())
_, userID := ts.InvitesDB.CreateArgsForCall(totalCreateCallCount - 1)
a.EqualValues(ts.User.ID, userID)
} else {
a.Equal(http.StatusForbidden, rec.Code)
r.Equal(totalCreateCallCount, ts.InvitesDB.CreateCallCount())
}
return rec
}
r.Equal(1, ts.InvitesDB.CreateCallCount(), "expected one invites.Create call")
_, userID := ts.InvitesDB.CreateArgsForCall(0)
a.EqualValues(ts.User.ID, userID)
rec := createInviteShouldWork(true)
doc, err := goquery.NewDocumentFromReader(rec.Body)
require.NoError(t, err, "failed to parse response")
@ -167,4 +179,34 @@ func TestInvitesCreate(t *testing.T) {
shownLink := doc.Find("#invite-facade-link").Text()
a.Equal(wantURL.String(), shownLink)
memberUser := roomdb.Member{
ID: 7331,
Role: roomdb.RoleMember,
PubKey: generatePubKey(),
}
modUser := roomdb.Member{
ID: 9001,
Role: roomdb.RoleModerator,
PubKey: generatePubKey(),
}
adminUser := roomdb.Member{
ID: 1337,
Role: roomdb.RoleAdmin,
PubKey: generatePubKey(),
}
/* test invite creation under various restricted mode with the roles member, mod, admin */
modes := []roomdb.PrivacyMode{roomdb.ModeRestricted, roomdb.ModeCommunity}
for _, mode := range modes {
ts.ConfigDB.GetPrivacyModeReturns(mode, nil)
ts.User = memberUser
// members can only invite in community rooms
createInviteShouldWork(mode == roomdb.ModeCommunity)
// mods & admins can always invite
ts.User = modUser
createInviteShouldWork(true)
ts.User = adminUser
createInviteShouldWork(true)
}
}

View File

@ -89,7 +89,7 @@ func TestNoticeAddLanguageOnlyAllowsPost(t *testing.T) {
// verify that a GET request is no bueno
u := ts.URLTo(router.AdminNoticeAddTranslation, "name", roomdb.NoticeNews.String())
_, resp := ts.Client.GetHTML(u)
a.Equal(http.StatusMethodNotAllowed, resp.Code, "GET should not be allowed for this route")
a.Equal(http.StatusBadRequest, resp.Code, "GET should not be allowed for this route")
// next up, we verify that a correct POST request actually works:
id := []string{"1"}

View File

@ -56,6 +56,14 @@ type testSession struct {
RoomState *roomstate.Manager
}
var pubKeyCount byte
func generatePubKey() refs.FeedRef {
pk := refs.FeedRef{Algo: "ed25519", ID: bytes.Repeat([]byte{pubKeyCount}, 32)}
pubKeyCount++
return pk
}
func newSession(t *testing.T) *testSession {
var ts testSession
@ -76,7 +84,7 @@ func newSession(t *testing.T) *testSession {
ts.netInfo = network.ServerEndpointDetails{
Domain: randutil.String(10),
RoomID: refs.FeedRef{Algo: "ed25519", ID: bytes.Repeat([]byte{0}, 32)},
RoomID: generatePubKey(),
UseSubdomainForAliases: true,
}
@ -97,8 +105,9 @@ func newSession(t *testing.T) *testSession {
// fake user
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleModerator,
ID: 1234,
Role: roomdb.RoleModerator,
PubKey: generatePubKey(),
}
testPath := filepath.Join("testrun", t.Name())
@ -121,10 +130,10 @@ func newSession(t *testing.T) *testSession {
os.MkdirAll(sessionsPath, 0700)
fsStore := sessions.NewFilesystemStore(sessionsPath, authKey, encKey)
// setup rendering
flashHelper := weberrs.NewFlashHelper(fsStore, locHelper)
// setup rendering
// template funcs
// TODO: make testing utils and move these there
testFuncs := web.TemplateFuncs(router, ts.netInfo)
testFuncs["current_page_is"] = func(routeName string) bool { return true }
@ -143,11 +152,13 @@ func newSession(t *testing.T) *testSession {
testFuncs["list_languages"] = func(*url.URL, string) string { return "" }
testFuncs["relative_time"] = func(when time.Time) string { return humanize.Time(when) }
eh := weberrs.NewErrorHandler(locHelper, flashHelper)
renderOpts := []render.Option{
render.SetLogger(log),
render.BaseTemplates("base.tmpl", "menu.tmpl", "flashes.tmpl"),
render.AddTemplates(append(HTMLTemplates, "error.tmpl")...),
render.ErrorTemplate("error.tmpl"),
render.SetErrorHandler(eh.Handle),
render.FuncMap(testFuncs),
}
renderOpts = append(renderOpts, locHelper.GetRenderFuncs()...)
@ -157,6 +168,8 @@ func newSession(t *testing.T) *testSession {
t.Fatal(errors.Wrap(err, "setup: render init failed"))
}
eh.SetRenderer(r)
handler := Handler(
ts.netInfo,
r,
@ -174,7 +187,7 @@ func newSession(t *testing.T) *testSession {
},
)
handler = members.MiddlewareForTests(ts.User)(handler)
handler = members.MiddlewareForTests(&ts.User)(handler)
ts.Mux = http.NewServeMux()
ts.Mux.Handle("/", handler)

View File

@ -88,4 +88,11 @@ func TestAliasResolve(t *testing.T) {
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")
a.Equal(ts.NetworkInfo.MultiserverAddress(), ar.MultiserverAddress)
/* alias resolving should not work for restricted rooms */
ts.ConfigDB.GetPrivacyModeReturns(roomdb.ModeRestricted, nil)
htmlURL, err = routes.Get(router.CompleteAliasResolve).URL("alias", testAlias.Name)
r.NoError(err)
html, resp = ts.Client.GetHTML(htmlURL)
a.Equal(http.StatusInternalServerError, resp.Code)
}

View File

@ -13,10 +13,10 @@ import (
// This is part of testing.go because we need to use roomMemberContextKey, which shouldn't be exported either.
// TODO: could be protected with an extra build tag.
// (Sadly +build test does not exist https://github.com/golang/go/issues/21360 )
func MiddlewareForTests(m roomdb.Member) func(http.Handler) http.Handler {
func MiddlewareForTests(m *roomdb.Member) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := context.WithValue(req.Context(), roomMemberContextKey, &m)
ctx := context.WithValue(req.Context(), roomMemberContextKey, m)
next.ServeHTTP(w, req.WithContext(ctx))
})
}