Browse Source

Merge pull request #148 from deathwish/restrict-room-creation

Restrict room creation
pull/154/head
Simon Eisenmann 12 years ago
parent
commit
8254693d0d
  1. 2
      doc/CHANNELING-API.txt
  2. 3
      server.conf.in
  3. 4
      src/app/spreed-webrtc-server/channeling.go
  4. 6
      src/app/spreed-webrtc-server/channelling_api.go
  5. 10
      src/app/spreed-webrtc-server/channelling_api_test.go
  6. 77
      src/app/spreed-webrtc-server/config.go
  7. 143
      src/app/spreed-webrtc-server/main.go
  8. 47
      src/app/spreed-webrtc-server/room_manager.go
  9. 32
      src/app/spreed-webrtc-server/room_manager_test.go
  10. 6
      src/app/spreed-webrtc-server/roomworker.go
  11. 4
      src/app/spreed-webrtc-server/roomworker_test.go
  12. 7
      src/app/spreed-webrtc-server/session.go

2
doc/CHANNELING-API.txt

@ -193,6 +193,8 @@ Special purpose documents for channling
authorization_not_required : No credentials should be provided for this authorization_not_required : No credentials should be provided for this
room. room.
invalid_credentials : The provided credentials are incorrect. invalid_credentials : The provided credentials are incorrect.
room_join_requires_account : Server configuration requires an
authenticated user account to join this room.
Welcome Welcome

3
server.conf.in

@ -78,6 +78,9 @@ encryptionSecret = tne-default-encryption-block-key
; all users will join this room if enabled. If it is disabled then a room join ; all users will join this room if enabled. If it is disabled then a room join
; form will be shown instead. ; form will be shown instead.
;defaultRoomEnabled = true ;defaultRoomEnabled = true
; Whether a user account is required to create a room. This only has an effect
; if user accounts are enabled. Optional, defaults to false.
;authorizeRoomCreation = false
; Server token is a public random string which is used to enhance security of ; Server token is a public random string which is used to enhance security of
; server generated security tokens. When the serverToken is changed all existing ; server generated security tokens. When the serverToken is changed all existing
; nonces become invalid. Use 32 or 64 characters (eg. 16 or 32 byte hex). ; nonces become invalid. Use 32 or 64 characters (eg. 16 or 32 byte hex).

4
src/app/spreed-webrtc-server/channeling.go

@ -27,6 +27,10 @@ type DataError struct {
Message string Message string
} }
func NewDataError(code, message string) error {
return &DataError{"Error", code, message}
}
func (err *DataError) Error() string { func (err *DataError) Error() string {
return err.Message return err.Message
} }

6
src/app/spreed-webrtc-server/channelling_api.go

@ -38,7 +38,6 @@ type ChannellingAPI interface {
} }
type channellingAPI struct { type channellingAPI struct {
version string
*Config *Config
RoomStatusManager RoomStatusManager
SessionEncoder SessionEncoder
@ -51,9 +50,8 @@ type channellingAPI struct {
buddyImages ImageCache buddyImages ImageCache
} }
func NewChannellingAPI(version string, config *Config, roomStatus RoomStatusManager, sessionEncoder SessionEncoder, sessionManager SessionManager, statsCounter StatsCounter, contactManager ContactManager, turnDataCreator TurnDataCreator, unicaster Unicaster, broadcaster Broadcaster, buddyImages ImageCache) ChannellingAPI { func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEncoder SessionEncoder, sessionManager SessionManager, statsCounter StatsCounter, contactManager ContactManager, turnDataCreator TurnDataCreator, unicaster Unicaster, broadcaster Broadcaster, buddyImages ImageCache) ChannellingAPI {
return &channellingAPI{ return &channellingAPI{
version,
config, config,
roomStatus, roomStatus,
sessionEncoder, sessionEncoder,
@ -255,7 +253,7 @@ func (api *channellingAPI) SendSelf(c Responder, session *Session) {
Userid: session.Userid(), Userid: session.Userid(),
Suserid: api.EncodeSessionUserID(session), Suserid: api.EncodeSessionUserID(session),
Token: token, Token: token,
Version: api.version, Version: api.Version,
Turn: api.CreateTurnData(session), Turn: api.CreateTurnData(session),
Stun: api.StunURIs, Stun: api.StunURIs,
} }

10
src/app/spreed-webrtc-server/channelling_api_test.go

@ -26,10 +26,6 @@ import (
"testing" "testing"
) )
const (
testAppVersion string = "0.0.0+unittests"
)
type fakeClient struct { type fakeClient struct {
replies map[string]interface{} replies map[string]interface{}
} }
@ -103,7 +99,7 @@ func assertErrorReply(t *testing.T, client *fakeClient, iid, code string) {
func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomManager) { func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomManager) {
client, roomManager, session := &fakeClient{}, &fakeRoomManager{}, &Session{} client, roomManager, session := &fakeClient{}, &fakeRoomManager{}, &Session{}
return NewChannellingAPI(testAppVersion, nil, roomManager, nil, nil, nil, nil, nil, nil, roomManager, nil), client, session, roomManager return NewChannellingAPI(nil, roomManager, nil, nil, nil, nil, nil, nil, roomManager, nil), client, session, roomManager
} }
func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing.T) { func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing.T) {
@ -199,7 +195,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t
func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) { func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
iid := "foo" iid := "foo"
api, client, session, roomManager := NewTestChannellingAPI() api, client, session, roomManager := NewTestChannellingAPI()
roomManager.joinError = &DataError{Type: "Error", Code: "bad_join"} roomManager.joinError = NewDataError("bad_join", "")
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: iid, Hello: &DataHello{}}) api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: iid, Hello: &DataHello{}})
@ -235,7 +231,7 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda
func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) { func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) {
iid, roomName := "123", "foo" iid, roomName := "123", "foo"
api, client, session, roomManager := NewTestChannellingAPI() api, client, session, roomManager := NewTestChannellingAPI()
roomManager.updateError = &DataError{Type: "Error", Code: "a_room_error", Message: ""} roomManager.updateError = NewDataError("a_room_error", "")
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: "0", Hello: &DataHello{Id: roomName}}) api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: "0", Hello: &DataHello{Id: roomName}})
api.OnIncoming(client, session, &DataIncoming{Type: "Room", Iid: iid, Room: &DataRoom{Name: roomName}}) api.OnIncoming(client, session, &DataIncoming{Type: "Room", Iid: iid, Room: &DataRoom{Name: roomName}})

77
src/app/spreed-webrtc-server/config.go

@ -24,6 +24,10 @@ package main
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"time"
"github.com/strukturag/phoenix"
) )
type Config struct { type Config struct {
@ -41,30 +45,79 @@ type Config struct {
UsersMode string // Users mode string UsersMode string // Users mode string
DefaultRoomEnabled bool // Flag if default room ("") is enabled DefaultRoomEnabled bool // Flag if default room ("") is enabled
Plugin string // Plugin to load Plugin string // Plugin to load
globalRoomid string // Id of the global room (not exported to Javascript) globalRoomID string // Id of the global room (not exported to Javascript)
authorizeRoomCreation bool // Whether a user account is required to create rooms (not exported to Javascript)
} }
func NewConfig(title, ver, runtimeVersion, basePath, serverToken string, stunURIs, turnURIs []string, tokens bool, globalRoomid string, defaultRoomEnabled, usersEnabled, usersAllowRegistration bool, usersMode, plugin string) *Config { func NewConfig(container phoenix.Container, tokens bool) *Config {
sv := fmt.Sprintf("static/ver=%s", ver) ver := container.GetStringDefault("app", "ver", "")
version := container.Version()
if version != "unreleased" {
ver = fmt.Sprintf("%s%s", ver, strings.Replace(version, ".", "", -1))
} else {
ts := fmt.Sprintf("%d", time.Now().Unix())
if ver == "" {
ver = ts
}
version = fmt.Sprintf("unreleased.%s", ts)
}
// Read base path from config and make sure it ends with a slash.
basePath := container.GetStringDefault("http", "basePath", "/")
if !strings.HasSuffix(basePath, "/") {
basePath = fmt.Sprintf("%s/", basePath)
}
if basePath != "/" {
container.Printf("Using '%s' base base path.", basePath)
}
//TODO(longsleep): When we have a database, generate this once from random source and store it.
serverToken := container.GetStringDefault("app", "serverToken", "i-did-not-change-the-public-token-boo")
stunURIsString := container.GetStringDefault("app", "stunURIs", "")
stunURIs := strings.Split(stunURIsString, " ")
trimAndRemoveDuplicates(&stunURIs)
turnURIsString := container.GetStringDefault("app", "turnURIs", "")
turnURIs := strings.Split(turnURIsString, " ")
trimAndRemoveDuplicates(&turnURIs)
return &Config{ return &Config{
Title: title, Title: container.GetStringDefault("app", "title", "Spreed WebRTC"),
ver: ver, ver: ver,
S: sv, S: fmt.Sprintf("static/ver=%s", ver),
B: basePath, B: basePath,
Token: serverToken, Token: serverToken,
StunURIs: stunURIs, StunURIs: stunURIs,
TurnURIs: turnURIs, TurnURIs: turnURIs,
Tokens: tokens, Tokens: tokens,
Version: runtimeVersion, Version: version,
UsersEnabled: usersEnabled, UsersEnabled: container.GetBoolDefault("users", "enabled", false),
UsersAllowRegistration: usersAllowRegistration, UsersAllowRegistration: container.GetBoolDefault("users", "allowRegistration", false),
UsersMode: usersMode, UsersMode: container.GetStringDefault("users", "mode", ""),
DefaultRoomEnabled: defaultRoomEnabled, DefaultRoomEnabled: container.GetBoolDefault("app", "defaultRoomEnabled", true),
Plugin: plugin, Plugin: container.GetStringDefault("app", "plugin", ""),
globalRoomid: globalRoomid, globalRoomID: container.GetStringDefault("app", "globalRoom", ""),
authorizeRoomCreation: container.GetBoolDefault("app", "authorizeRoomCreation", false),
} }
} }
func (config *Config) Get(request *http.Request) (int, interface{}, http.Header) { func (config *Config) Get(request *http.Request) (int, interface{}, http.Header) {
return 200, config, http.Header{"Content-Type": {"application/json; charset=utf-8"}} return 200, config, http.Header{"Content-Type": {"application/json; charset=utf-8"}}
} }
// Helper function to clean up string arrays.
func trimAndRemoveDuplicates(data *[]string) {
found := make(map[string]bool)
j := 0
for i, x := range *data {
x = strings.TrimSpace(x)
if len(x) > 0 && !found[x] {
found[x] = true
(*data)[j] = (*data)[i]
j++
}
}
*data = (*data)[:j]
}

143
src/app/spreed-webrtc-server/main.go

@ -40,7 +40,6 @@ import (
"path" "path"
goruntime "runtime" goruntime "runtime"
"strconv" "strconv"
"strings"
"syscall" "syscall"
"time" "time"
) )
@ -63,21 +62,6 @@ func getRequestLanguages(r *http.Request, supportedLanguages []string) []string
} }
// Helper function to clean up string arrays.
func trimAndRemoveDuplicates(data *[]string) {
found := make(map[string]bool)
j := 0
for i, x := range *data {
x = strings.TrimSpace(x)
if len(x) > 0 && !found[x] {
found[x] = true
(*data)[j] = (*data)[i]
j++
}
}
*data = (*data)[:j]
}
func mainHandler(w http.ResponseWriter, r *http.Request) { func mainHandler(w http.ResponseWriter, r *http.Request) {
handleRoomView("", w, r) handleRoomView("", w, r)
@ -184,17 +168,6 @@ func runner(runtime phoenix.Runtime) error {
return fmt.Errorf("Unable to find client. Path correct and compiled css?") return fmt.Errorf("Unable to find client. Path correct and compiled css?")
} }
// Read base path from config and make sure it ends with a slash.
basePath, err := runtime.GetString("http", "basePath")
if err != nil {
basePath = "/"
} else {
if !strings.HasSuffix(basePath, "/") {
basePath = fmt.Sprintf("%s/", basePath)
}
log.Printf("Using '%s' base base path.", basePath)
}
statsEnabled, err := runtime.GetBool("http", "stats") statsEnabled, err := runtime.GetBool("http", "stats")
if err != nil { if err != nil {
statsEnabled = false statsEnabled = false
@ -246,109 +219,33 @@ func runner(runtime phoenix.Runtime) error {
} }
} }
tokenFile, err := runtime.GetString("app", "tokenFile")
if err == nil {
if !httputils.HasFilePath(path.Clean(tokenFile)) {
return fmt.Errorf("Unable to find token file at %s", tokenFile)
}
}
title, err := runtime.GetString("app", "title")
if err != nil {
title = "Spreed WebRTC"
}
ver, err := runtime.GetString("app", "ver")
if err != nil {
ver = ""
}
runtimeVersion := version
if version != "unreleased" {
ver1 := ver
if err != nil {
ver1 = ""
}
ver = fmt.Sprintf("%s%s", ver1, strings.Replace(version, ".", "", -1))
} else {
ts := fmt.Sprintf("%d", time.Now().Unix())
if err != nil {
ver = ts
}
runtimeVersion = fmt.Sprintf("unreleased.%s", ts)
}
turnURIsString, err := runtime.GetString("app", "turnURIs")
if err != nil {
turnURIsString = ""
}
turnURIs := strings.Split(turnURIsString, " ")
trimAndRemoveDuplicates(&turnURIs)
var turnSecret []byte var turnSecret []byte
turnSecretString, err := runtime.GetString("app", "turnSecret") turnSecretString, err := runtime.GetString("app", "turnSecret")
if err == nil { if err == nil {
turnSecret = []byte(turnSecretString) turnSecret = []byte(turnSecretString)
} }
stunURIsString, err := runtime.GetString("app", "stunURIs") serverRealm, err := runtime.GetString("app", "serverRealm")
if err != nil {
stunURIsString = ""
}
stunURIs := strings.Split(stunURIsString, " ")
trimAndRemoveDuplicates(&stunURIs)
globalRoomid, err := runtime.GetString("app", "globalRoom")
if err != nil {
// Global room is disabled.
globalRoomid = ""
}
plugin, err := runtime.GetString("app", "plugin")
if err != nil { if err != nil {
plugin = "" serverRealm = "local"
}
defaultRoomEnabled := true
defaultRoomEnabledString, err := runtime.GetString("app", "defaultRoomEnabled")
if err == nil {
defaultRoomEnabled = defaultRoomEnabledString == "true"
}
usersEnabled := false
usersEnabledString, err := runtime.GetString("users", "enabled")
if err == nil {
usersEnabled = usersEnabledString == "true"
} }
usersAllowRegistration := false // Create token provider.
usersAllowRegistrationString, err := runtime.GetString("users", "allowRegistration") tokenFile, err := runtime.GetString("app", "tokenFile")
if err == nil { if err == nil {
usersAllowRegistration = usersAllowRegistrationString == "true" if !httputils.HasFilePath(path.Clean(tokenFile)) {
} return fmt.Errorf("Unable to find token file at %s", tokenFile)
serverToken, err := runtime.GetString("app", "serverToken")
if err != nil {
//TODO(longsleep): When we have a database, generate this once from random source and store it.
serverToken = "i-did-not-change-the-public-token-boo"
} }
serverRealm, err := runtime.GetString("app", "serverRealm")
if err != nil {
serverRealm = "local"
} }
usersMode, _ := runtime.GetString("users", "mode")
// Create token provider.
var tokenProvider TokenProvider var tokenProvider TokenProvider
if tokenFile != "" { if tokenFile != "" {
log.Printf("Using token authorization from %s\n", tokenFile) log.Printf("Using token authorization from %s\n", tokenFile)
tokenProvider = TokenFileProvider(tokenFile) tokenProvider = TokenFileProvider(tokenFile)
} }
// Create configuration data structure. // Load remaining configuration items.
config = NewConfig(title, ver, runtimeVersion, basePath, serverToken, stunURIs, turnURIs, tokenProvider != nil, globalRoomid, defaultRoomEnabled, usersEnabled, usersAllowRegistration, usersMode, plugin) config = NewConfig(runtime, tokenProvider != nil)
// Load templates. // Load templates.
tt := template.New("") tt := template.New("")
@ -373,7 +270,7 @@ func runner(runtime phoenix.Runtime) error {
} }
// Create realm string from config. // Create realm string from config.
computedRealm := fmt.Sprintf("%s.%s", serverRealm, serverToken) computedRealm := fmt.Sprintf("%s.%s", serverRealm, config.Token)
// Set number of go routines if it is 1 // Set number of go routines if it is 1
if goruntime.GOMAXPROCS(0) == 1 { if goruntime.GOMAXPROCS(0) == 1 {
@ -406,7 +303,7 @@ func runner(runtime phoenix.Runtime) error {
// Create router. // Create router.
router := mux.NewRouter() router := mux.NewRouter()
r := router.PathPrefix(basePath).Subrouter().StrictSlash(true) r := router.PathPrefix(config.B).Subrouter().StrictSlash(true)
// HTTP listener support. // HTTP listener support.
if _, err = runtime.GetString("http", "listen"); err == nil { if _, err = runtime.GetString("http", "listen"); err == nil {
@ -434,12 +331,12 @@ func runner(runtime phoenix.Runtime) error {
tickets := NewTickets(sessionSecret, encryptionSecret, computedRealm) tickets := NewTickets(sessionSecret, encryptionSecret, computedRealm)
sessionManager := NewSessionManager(config, tickets, sessionSecret) sessionManager := NewSessionManager(config, tickets, sessionSecret)
statsManager := NewStatsManager(hub, roomManager, sessionManager) statsManager := NewStatsManager(hub, roomManager, sessionManager)
channellingAPI := NewChannellingAPI(runtimeVersion, config, roomManager, tickets, sessionManager, statsManager, hub, hub, hub, roomManager, buddyImages) channellingAPI := NewChannellingAPI(config, roomManager, tickets, sessionManager, statsManager, hub, hub, hub, roomManager, buddyImages)
r.HandleFunc("/", httputils.MakeGzipHandler(mainHandler)) r.HandleFunc("/", httputils.MakeGzipHandler(mainHandler))
r.Handle("/static/img/buddy/{flags}/{imageid}/{idx:.*}", http.StripPrefix(basePath, makeImageHandler(buddyImages, time.Duration(24)*time.Hour))) r.Handle("/static/img/buddy/{flags}/{imageid}/{idx:.*}", http.StripPrefix(config.B, makeImageHandler(buddyImages, time.Duration(24)*time.Hour)))
r.Handle("/static/{path:.*}", http.StripPrefix(basePath, httputils.FileStaticServer(http.Dir(rootFolder)))) r.Handle("/static/{path:.*}", http.StripPrefix(config.B, httputils.FileStaticServer(http.Dir(rootFolder))))
r.Handle("/robots.txt", http.StripPrefix(basePath, http.FileServer(http.Dir(path.Join(rootFolder, "static"))))) r.Handle("/robots.txt", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static")))))
r.Handle("/favicon.ico", http.StripPrefix(basePath, http.FileServer(http.Dir(path.Join(rootFolder, "static", "img"))))) r.Handle("/favicon.ico", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static", "img")))))
r.Handle("/ws", makeWSHandler(statsManager, sessionManager, codec, channellingAPI)) r.Handle("/ws", makeWSHandler(statsManager, sessionManager, codec, channellingAPI))
r.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler)) r.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler))
@ -449,11 +346,11 @@ func runner(runtime phoenix.Runtime) error {
api.AddResource(&Rooms{}, "/rooms") api.AddResource(&Rooms{}, "/rooms")
api.AddResource(config, "/config") api.AddResource(config, "/config")
api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens") api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens")
if usersEnabled { if config.UsersEnabled {
// Create Users handler. // Create Users handler.
users := NewUsers(hub, tickets, sessionManager, usersMode, serverRealm, runtime) users := NewUsers(hub, tickets, sessionManager, config.UsersMode, serverRealm, runtime)
api.AddResource(&Sessions{tickets, hub, users}, "/sessions/{id}/") api.AddResource(&Sessions{tickets, hub, users}, "/sessions/{id}/")
if usersAllowRegistration { if config.UsersAllowRegistration {
api.AddResource(users, "/users") api.AddResource(users, "/users")
} }
} }
@ -466,7 +363,7 @@ func runner(runtime phoenix.Runtime) error {
if extraFolder != "" { if extraFolder != "" {
extraFolderStatic := path.Join(extraFolder, "static") extraFolderStatic := path.Join(extraFolder, "static")
if _, err = os.Stat(extraFolderStatic); err == nil { if _, err = os.Stat(extraFolderStatic); err == nil {
r.Handle("/extra/static/{path:.*}", http.StripPrefix(fmt.Sprintf("%sextra", basePath), httputils.FileStaticServer(http.Dir(extraFolder)))) r.Handle("/extra/static/{path:.*}", http.StripPrefix(fmt.Sprintf("%sextra", config.B), httputils.FileStaticServer(http.Dir(extraFolder))))
log.Printf("Added URL handler /extra/static/... for static files in %s/...\n", extraFolderStatic) log.Printf("Added URL handler /extra/static/... for static files in %s/...\n", extraFolderStatic)
} }
} }
@ -491,7 +388,7 @@ func boot() error {
return nil return nil
} }
return phoenix.NewServer("server", ""). return phoenix.NewServer("server", version).
Config(configPath). Config(configPath).
Log(logPath). Log(logPath).
CpuProfile(cpuprofile). CpuProfile(cpuprofile).

47
src/app/spreed-webrtc-server/room_manager.go

@ -49,18 +49,16 @@ type RoomManager interface {
type roomManager struct { type roomManager struct {
sync.RWMutex sync.RWMutex
*Config
OutgoingEncoder OutgoingEncoder
defaultRoomEnabled bool
globalRoomID string
roomTable map[string]RoomWorker roomTable map[string]RoomWorker
} }
func NewRoomManager(config *Config, encoder OutgoingEncoder) RoomManager { func NewRoomManager(config *Config, encoder OutgoingEncoder) RoomManager {
return &roomManager{ return &roomManager{
sync.RWMutex{}, sync.RWMutex{},
config,
encoder, encoder,
config.DefaultRoomEnabled,
config.globalRoomid,
make(map[string]RoomWorker), make(map[string]RoomWorker),
} }
} }
@ -74,11 +72,16 @@ func (rooms *roomManager) RoomUsers(session *Session) []*DataSession {
} }
func (rooms *roomManager) JoinRoom(id string, credentials *DataRoomCredentials, session *Session, sender Sender) (*DataRoom, error) { func (rooms *roomManager) JoinRoom(id string, credentials *DataRoomCredentials, session *Session, sender Sender) (*DataRoom, error) {
if id == "" && !rooms.defaultRoomEnabled { if id == "" && !rooms.DefaultRoomEnabled {
return nil, &DataError{Type: "Error", Code: "default_room_disabled", Message: "The default room is not enabled"} return nil, NewDataError("default_room_disabled", "The default room is not enabled")
} }
return rooms.GetOrCreate(id, credentials).Join(credentials, session, sender) roomWorker, err := rooms.GetOrCreate(id, credentials, session)
if err != nil {
return nil, err
}
return roomWorker.Join(credentials, session, sender)
} }
func (rooms *roomManager) LeaveRoom(session *Session) { func (rooms *roomManager) LeaveRoom(session *Session) {
@ -89,7 +92,7 @@ func (rooms *roomManager) LeaveRoom(session *Session) {
func (rooms *roomManager) UpdateRoom(session *Session, room *DataRoom) (*DataRoom, error) { func (rooms *roomManager) UpdateRoom(session *Session, room *DataRoom) (*DataRoom, error) {
if !session.Hello || session.Roomid != room.Name { if !session.Hello || session.Roomid != room.Name {
return nil, &DataError{Type: "Error", Code: "not_in_room", Message: "Cannot update other rooms"} return nil, NewDataError("not_in_room", "Cannot update other rooms")
} }
// XXX(lcooper): We'll process and send documents without this field // XXX(lcooper): We'll process and send documents without this field
// correctly, however clients cannot not handle it currently. // correctly, however clients cannot not handle it currently.
@ -149,15 +152,25 @@ func (rooms *roomManager) Get(id string) (room RoomWorker, ok bool) {
return return
} }
func (rooms *roomManager) GetOrCreate(id string, credentials *DataRoomCredentials) RoomWorker { func (rooms *roomManager) GetOrCreate(id string, credentials *DataRoomCredentials, session *Session) (RoomWorker, error) {
room, ok := rooms.Get(id) if room, ok := rooms.Get(id); ok {
if !ok { return room, nil
}
rooms.Lock() rooms.Lock()
// Need to re-check, another thread might have created the room // Need to re-check, another thread might have created the room
// while we waited for the lock. // while we waited for the lock.
room, ok = rooms.roomTable[id] if room, ok := rooms.roomTable[id]; ok {
if !ok { rooms.Unlock()
room = NewRoomWorker(rooms, id, credentials) return room, nil
}
if rooms.UsersEnabled && rooms.authorizeRoomCreation && !session.Authenticated() {
rooms.Unlock()
return nil, NewDataError("room_join_requires_account", "Room creation requires a user account")
}
room := NewRoomWorker(rooms, id, credentials)
rooms.roomTable[id] = room rooms.roomTable[id] = room
rooms.Unlock() rooms.Unlock()
go func() { go func() {
@ -169,12 +182,8 @@ func (rooms *roomManager) GetOrCreate(id string, credentials *DataRoomCredential
delete(rooms.roomTable, id) delete(rooms.roomTable, id)
log.Printf("Cleaned up room '%s'\n", id) log.Printf("Cleaned up room '%s'\n", id)
}() }()
} else {
rooms.Unlock()
}
}
return room return room, nil
} }
func (rooms *roomManager) GlobalUsers() []*roomUser { func (rooms *roomManager) GlobalUsers() []*roomUser {

32
src/app/spreed-webrtc-server/room_manager_test.go

@ -25,26 +25,48 @@ import (
"testing" "testing"
) )
func NewTestRoomManager() RoomManager { func NewTestRoomManager() (RoomManager, *Config) {
return NewRoomManager(&Config{}, nil) config := &Config{}
return NewRoomManager(config, nil), config
}
func Test_RoomManager_JoinRoom_ReturnsAnErrorForUnauthenticatedSessionsWhenCreationRequiresAnAccount(t *testing.T) {
roomManager, config := NewTestRoomManager()
config.UsersEnabled = true
config.authorizeRoomCreation = true
unauthenticatedSession := &Session{}
_, err := roomManager.JoinRoom("foo", nil, unauthenticatedSession, nil)
assertDataError(t, err, "room_join_requires_account")
authenticatedSession := &Session{userid: "9870457"}
_, err = roomManager.JoinRoom("foo", nil, authenticatedSession, nil)
if err != nil {
t.Fatalf("Unexpected error %v joining room while authenticated", err)
}
_, err = roomManager.JoinRoom("foo", nil, unauthenticatedSession, nil)
if err != nil {
t.Fatalf("Unexpected error %v joining room while unauthenticated", err)
}
} }
func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfNoRoomHasBeenJoined(t *testing.T) { func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfNoRoomHasBeenJoined(t *testing.T) {
roomManager := NewTestRoomManager() roomManager, _ := NewTestRoomManager()
_, err := roomManager.UpdateRoom(&Session{}, nil) _, err := roomManager.UpdateRoom(&Session{}, nil)
assertDataError(t, err, "not_in_room") assertDataError(t, err, "not_in_room")
} }
func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfUpdatingAnUnjoinedRoom(t *testing.T) { func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfUpdatingAnUnjoinedRoom(t *testing.T) {
roomManager := NewTestRoomManager() roomManager, _ := NewTestRoomManager()
session := &Session{Hello: true, Roomid: "foo"} session := &Session{Hello: true, Roomid: "foo"}
_, err := roomManager.UpdateRoom(session, &DataRoom{Name: "bar"}) _, err := roomManager.UpdateRoom(session, &DataRoom{Name: "bar"})
assertDataError(t, err, "not_in_room") assertDataError(t, err, "not_in_room")
} }
func Test_RoomManager_UpdateRoom_ReturnsACorrectlyTypedDocument(t *testing.T) { func Test_RoomManager_UpdateRoom_ReturnsACorrectlyTypedDocument(t *testing.T) {
roomManager := NewTestRoomManager() roomManager, _ := NewTestRoomManager()
session := &Session{Hello: true, Roomid: "foo"} session := &Session{Hello: true, Roomid: "foo"}
room, err := roomManager.UpdateRoom(session, &DataRoom{Name: session.Roomid}) room, err := roomManager.UpdateRoom(session, &DataRoom{Name: session.Roomid})
if err != nil { if err != nil {

6
src/app/spreed-webrtc-server/roomworker.go

@ -244,18 +244,18 @@ func (r *roomWorker) Join(credentials *DataRoomCredentials, session *Session, se
worker := func() { worker := func() {
r.mutex.Lock() r.mutex.Lock()
if r.credentials == nil && credentials != nil { if r.credentials == nil && credentials != nil {
results <- joinResult{nil, &DataError{"Error", "authorization_not_required", "No credentials may be provided for this room"}} results <- joinResult{nil, NewDataError("authorization_not_required", "No credentials may be provided for this room")}
r.mutex.Unlock() r.mutex.Unlock()
return return
} else if r.credentials != nil { } else if r.credentials != nil {
if credentials == nil { if credentials == nil {
results <- joinResult{nil, &DataError{"Error", "authorization_required", "Valid credentials are required to join this room"}} results <- joinResult{nil, NewDataError("authorization_required", "Valid credentials are required to join this room")}
r.mutex.Unlock() r.mutex.Unlock()
return return
} }
if len(r.credentials.PIN) != len(credentials.PIN) || subtle.ConstantTimeCompare([]byte(r.credentials.PIN), []byte(credentials.PIN)) != 1 { if len(r.credentials.PIN) != len(credentials.PIN) || subtle.ConstantTimeCompare([]byte(r.credentials.PIN), []byte(credentials.PIN)) != 1 {
results <- joinResult{nil, &DataError{"Error", "invalid_credentials", "The provided credentials are incorrect"}} results <- joinResult{nil, NewDataError("invalid_credentials", "The provided credentials are incorrect")}
r.mutex.Unlock() r.mutex.Unlock()
return return
} }

4
src/app/spreed-webrtc-server/roomworker_test.go

@ -30,14 +30,14 @@ const (
) )
func NewTestRoomWorker() RoomWorker { func NewTestRoomWorker() RoomWorker {
worker := NewRoomWorker(&roomManager{}, testRoomName, nil) worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomName, nil)
go worker.Start() go worker.Start()
return worker return worker
} }
func NewTestRoomWorkerWithPIN(t *testing.T) (RoomWorker, string) { func NewTestRoomWorkerWithPIN(t *testing.T) (RoomWorker, string) {
pin := "asdf" pin := "asdf"
worker := NewRoomWorker(&roomManager{}, testRoomName, &DataRoomCredentials{PIN: pin}) worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomName, &DataRoomCredentials{PIN: pin})
go worker.Start() go worker.Start()
return worker, pin return worker, pin
} }

7
src/app/spreed-webrtc-server/session.go

@ -173,6 +173,13 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {
} }
func (s *Session) Authenticated() (authenticated bool) {
s.mutex.Lock()
authenticated = s.userid != ""
s.mutex.Unlock()
return
}
func (s *Session) Authenticate(realm string, st *SessionToken, userid string) error { func (s *Session) Authenticate(realm string, st *SessionToken, userid string) error {
s.mutex.Lock() s.mutex.Lock()

Loading…
Cancel
Save