get started with privacy modes, parse -mode flag

This commit is contained in:
cblgh 2021-03-24 10:58:32 +01:00
parent dafc341091
commit faae7c0324
13 changed files with 161 additions and 17 deletions

View File

@ -34,6 +34,7 @@ import (
"github.com/ssb-ngi-pointer/go-ssb-room/internal/network"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/repo"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/signinwithssb"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb/sqlite"
"github.com/ssb-ngi-pointer/go-ssb-room/roomsrv"
mksrv "github.com/ssb-ngi-pointer/go-ssb-room/roomsrv"
@ -53,6 +54,8 @@ var (
logToFile string
repoDir string
config roomdb.RoomConfig
// helper
log kitlog.Logger
@ -81,9 +84,23 @@ func checkAndLog(err error) {
}
}
type Config struct {
// open, community, restricted
privacyMode roomdb.PrivacyMode
}
func (c Config) GetPrivacyMode(ctx context.Context) (roomdb.PrivacyMode, error) {
err := c.privacyMode.IsValid()
if err != nil {
return roomdb.ModeUnknown, err
}
return c.privacyMode, nil
}
func initFlags() {
u, err := user.Current()
checkFatal(err)
config = Config{privacyMode: roomdb.ModeCommunity} // set default privacy mode to community
flag.StringVar(&appKey, "shscap", "1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=", "secret-handshake app-key (or capability)")
@ -101,6 +118,16 @@ func initFlags() {
flag.BoolVar(&flagPrintVersion, "version", false, "print version number and build date")
flag.Func("mode", "the privacy mode (values: open, community, restricted) determining room access controls", func(val string) error {
privacyMode := roomdb.ParsePrivacyMode(val)
err := privacyMode.IsValid()
if err != nil {
return fmt.Errorf("%s, valid values are open, community, restricted", err)
}
config = Config{privacyMode: privacyMode}
return nil
})
flag.Parse()
if logToFile != "" {
@ -215,6 +242,7 @@ func runroomsrv() error {
db.Aliases,
db.AuthWithSSB,
bridge,
config,
httpsDomain,
opts...)
if err != nil {
@ -264,6 +292,7 @@ func runroomsrv() error {
Aliases: db.Aliases,
AuthFallback: db.AuthFallback,
AuthWithSSB: db.AuthWithSSB,
Config: config,
DeniedKeys: db.DeniedKeys,
Invites: db.Invites,
Notices: db.Notices,

View File

@ -7,6 +7,7 @@ import (
"go.cryptoscope.co/muxrpc/v2"
"go.cryptoscope.co/muxrpc/v2/typemux"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
"github.com/ssb-ngi-pointer/go-ssb-room/roomstate"
refs "go.mindeco.de/ssb-refs"
)
@ -22,11 +23,13 @@ import (
}
*/
func New(log kitlog.Logger, self refs.FeedRef, m *roomstate.Manager) *Handler {
func New(log kitlog.Logger, self refs.FeedRef, m *roomstate.Manager, members roomdb.MembersService, config roomdb.RoomConfig) *Handler {
var h = new(Handler)
h.self = self
h.logger = log
h.state = m
h.members = members
h.config = config
return h
}

View File

@ -5,9 +5,11 @@ package server
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/network"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
"github.com/ssb-ngi-pointer/go-ssb-room/roomstate"
refs "go.mindeco.de/ssb-refs"
@ -20,7 +22,9 @@ type Handler struct {
logger kitlog.Logger
self refs.FeedRef
state *roomstate.Manager
state *roomstate.Manager
members roomdb.MembersService
config roomdb.RoomConfig
}
func (h *Handler) isRoom(context.Context, *muxrpc.Request) (interface{}, error) {
@ -57,7 +61,7 @@ func (h *Handler) leave(_ context.Context, req *muxrpc.Request) (interface{}, er
return false, nil
}
func (h *Handler) endpoints(_ context.Context, req *muxrpc.Request, snk *muxrpc.ByteSink) error {
func (h *Handler) endpoints(ctx context.Context, req *muxrpc.Request, snk *muxrpc.ByteSink) error {
level.Debug(h.logger).Log("called", "endpoints")
toPeer := newForwarder(snk)
@ -70,6 +74,21 @@ func (h *Handler) endpoints(_ context.Context, req *muxrpc.Request, snk *muxrpc.
return err
}
pm, err := h.config.GetPrivacyMode(ctx)
if err != nil {
return fmt.Errorf("running with unknown privacy mode")
}
switch pm {
case roomdb.ModeCommunity:
fallthrough
case roomdb.ModeRestricted:
_, err := h.members.GetByFeed(ctx, *ref)
if err != nil {
return fmt.Errorf("external user are not allowed to enumerate members")
}
}
has := h.state.AlreadyAdded(*ref, req.Endpoint())
if !has {
// just send the current state to the new peer

View File

@ -17,6 +17,10 @@ import (
refs "go.mindeco.de/ssb-refs"
)
type RoomConfig interface {
GetPrivacyMode(context.Context) (PrivacyMode, error)
}
// AuthFallbackService allows password authentication which might be helpful for scenarios
// where one lost access to his ssb device or key.
type AuthFallbackService interface {

View File

@ -33,6 +33,41 @@ type Member struct {
PubKey refs.FeedRef
}
//go:generate go run golang.org/x/tools/cmd/stringer -type=PrivacyMode
type PrivacyMode uint
func (pm PrivacyMode) IsValid() error {
if pm == ModeUnknown || pm > ModeRestricted {
return errors.New("No such privacy mode")
}
return nil
}
func ParsePrivacyMode(val string) PrivacyMode {
switch val {
case "open":
return ModeOpen
case "community":
return ModeCommunity
case "restricted":
return ModeRestricted
default:
return ModeUnknown
}
}
// PrivacyMode describes the access mode the room server is currently running under.
// ModeOpen allows anyone to create an room invite
// ModeCommunity restricts invite creation to pre-existing room members (i.e. "internal users")
// ModeRestricted only allows admins and moderators to create room invitations
const (
ModeUnknown PrivacyMode = iota
ModeOpen
ModeCommunity
ModeRestricted
)
//go:generate go run golang.org/x/tools/cmd/stringer -type=Role
// Role describes the authorization level of an internal user (or member).
@ -42,7 +77,7 @@ type Role uint
func (r Role) IsValid() error {
if r == RoleUnknown {
return errors.New("uknown member role")
return errors.New("unknown member role")
}
if r > RoleAdmin {
return errors.New("invalid member role")

View File

@ -23,6 +23,8 @@ func (s *Server) initHandlers() {
kitlog.With(s.logger, "unit", "tunnel"),
s.Whoami(),
s.StateManager,
s.Members,
s.Config,
)
aliasHandler := alias.New(

View File

@ -9,6 +9,7 @@ import (
"go.cryptoscope.co/muxrpc/v2"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/network"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
)
// opens the shs listener for TCP connections
@ -27,11 +28,20 @@ func (s *Server) initNetwork() error {
return &s.master, nil
}
if _, err := s.authorizer.GetByFeed(s.rootCtx, *remote); err == nil {
return &s.public, nil
pm, err := s.Config.GetPrivacyMode(nil)
if err != nil {
return nil, fmt.Errorf("running with unknown privacy mode")
}
return nil, fmt.Errorf("not authorized")
// if privacy mode is restricted, deny connections from non-members
if pm == roomdb.ModeRestricted {
if _, err := s.authorizer.GetByFeed(s.rootCtx, *remote); err != nil {
return nil, fmt.Errorf("access restricted to members")
}
}
// for community + open modes, allow all connections
return &s.public, nil
}
// tcp+shs

View File

@ -70,6 +70,7 @@ type Server struct {
authWithSSB roomdb.AuthWithSSBService
authWithSSBBridge *signinwithssb.SignalBridge
Config roomdb.RoomConfig
}
func (s Server) Whoami() refs.FeedRef {
@ -81,6 +82,7 @@ func New(
aliasdb roomdb.AliasesService,
awsdb roomdb.AuthWithSSBService,
bridge *signinwithssb.SignalBridge,
config roomdb.RoomConfig,
domainName string,
opts ...Option,
) (*Server, error) {
@ -89,6 +91,7 @@ func New(
s.Members = membersdb
s.Aliases = aliasdb
s.Config = config
s.authWithSSB = awsdb
s.authWithSSBBridge = bridge

View File

@ -44,6 +44,7 @@ var HTMLTemplates = []string{
// Databases is an option struct that encapsualtes the required database services
type Databases struct {
Aliases roomdb.AliasesService
Config roomdb.RoomConfig // cblgh: kind of confusing that we have two identically named structs, in different http handler contexts?
DeniedKeys roomdb.DeniedKeysService
Invites roomdb.InvitesService
Notices roomdb.NoticesService
@ -117,11 +118,13 @@ func Handler(
mux.HandleFunc("/members/remove", mh.remove)
var ih = invitesHandler{
r: r,
db: dbs.Invites,
r: r,
db: dbs.Invites,
config: dbs.Config,
domainName: domainName,
}
mux.HandleFunc("/invites", r.HTML("admin/invite-list.tmpl", ih.overview))
mux.HandleFunc("/invites/create", r.HTML("admin/invite-created.tmpl", ih.create))
mux.HandleFunc("/invites/revoke/confirm", r.HTML("admin/invite-revoke-confirm.tmpl", ih.revokeConfirm))

View File

@ -19,7 +19,8 @@ import (
type invitesHandler struct {
r *render.Renderer
db roomdb.InvitesService
db roomdb.InvitesService
config roomdb.RoomConfig
domainName string
}
@ -58,6 +59,26 @@ func (h invitesHandler) create(w http.ResponseWriter, req *http.Request) (interf
if member == nil {
return nil, fmt.Errorf("warning: no user session for elevated access request")
}
pm, err := h.config.GetPrivacyMode(req.Context())
if err != nil {
return nil, err
}
/* We want to check:
* 1. the room's privacy mode
* 2. the role of the member trying to create the invite
* and deny unallowed requests (e.g. member creating invite in ModeRestricted)
*/
switch pm {
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")
}
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")
}
}
token, err := h.db.Create(req.Context(), member.ID)
if err != nil {

View File

@ -21,12 +21,13 @@ import (
type aliasHandler struct {
r *render.Renderer
db roomdb.AliasesService
db roomdb.AliasesService
config roomdb.RoomConfig
roomEndpoint network.ServerEndpointDetails
}
func (a aliasHandler) resolve(rw http.ResponseWriter, req *http.Request) {
func (h aliasHandler) resolve(rw http.ResponseWriter, req *http.Request) {
respEncoding := req.URL.Query().Get("encoding")
var ar aliasResponder
@ -34,18 +35,28 @@ func (a aliasHandler) resolve(rw http.ResponseWriter, req *http.Request) {
case "json":
ar = newAliasJSONResponder(rw)
default:
ar = newAliasHTMLResponder(a.r, rw, req)
ar = newAliasHTMLResponder(h.r, rw, req)
}
ar.UpdateRoomInfo(a.roomEndpoint)
pm, err := h.config.GetPrivacyMode(req.Context())
if err != nil {
ar.SendError(fmt.Errorf("room is running an unknown privacy mode"))
return
}
if pm == roomdb.ModeRestricted {
ar.SendError(fmt.Errorf("this room is restricted, alias resolving is turned off"))
return
}
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)
alias, err := h.db.Resolve(req.Context(), name)
if err != nil {
ar.SendError(fmt.Errorf("aliases: failed to resolve name %q: %w", name, err))
return

View File

@ -53,6 +53,7 @@ type Databases struct {
Aliases roomdb.AliasesService
AuthFallback roomdb.AuthFallbackService
AuthWithSSB roomdb.AuthWithSSBService
Config roomdb.RoomConfig
DeniedKeys roomdb.DeniedKeysService
Invites roomdb.InvitesService
Notices roomdb.NoticesService
@ -260,6 +261,7 @@ func New(
roomState,
admin.Databases{
Aliases: dbs.Aliases,
Config: dbs.Config,
DeniedKeys: dbs.DeniedKeys,
Invites: dbs.Invites,
Notices: dbs.Notices,
@ -294,7 +296,8 @@ func New(
var ah = aliasHandler{
r: r,
db: dbs.Aliases,
db: dbs.Aliases,
config: dbs.Config,
roomEndpoint: netInfo,
}
@ -303,8 +306,8 @@ func New(
var ih = inviteHandler{
render: r,
invites: dbs.Invites,
pinnedNotices: dbs.PinnedNotices,
config: dbs.Config,
invites: dbs.Invites,
networkInfo: netInfo,
}

View File

@ -29,6 +29,7 @@ type inviteHandler struct {
invites roomdb.InvitesService
pinnedNotices roomdb.PinnedNoticesService
config roomdb.RoomConfig
networkInfo network.ServerEndpointDetails
}