From 750951170498da40118bae8ac31f0428589cdecc Mon Sep 17 00:00:00 2001 From: Lance Cooper Date: Fri, 14 Nov 2014 11:35:54 -0600 Subject: [PATCH 1/2] Use config directly in room manager and centralize config loading. --- .../spreed-webrtc-server/channelling_api.go | 6 +- .../channelling_api_test.go | 6 +- src/app/spreed-webrtc-server/config.go | 75 +++++++-- src/app/spreed-webrtc-server/main.go | 145 +++--------------- src/app/spreed-webrtc-server/room_manager.go | 10 +- .../spreed-webrtc-server/roomworker_test.go | 4 +- 6 files changed, 93 insertions(+), 153 deletions(-) diff --git a/src/app/spreed-webrtc-server/channelling_api.go b/src/app/spreed-webrtc-server/channelling_api.go index 832a79ae..e790021f 100644 --- a/src/app/spreed-webrtc-server/channelling_api.go +++ b/src/app/spreed-webrtc-server/channelling_api.go @@ -38,7 +38,6 @@ type ChannellingAPI interface { } type channellingAPI struct { - version string *Config RoomStatusManager SessionEncoder @@ -51,9 +50,8 @@ type channellingAPI struct { 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{ - version, config, roomStatus, sessionEncoder, @@ -255,7 +253,7 @@ func (api *channellingAPI) SendSelf(c Responder, session *Session) { Userid: session.Userid(), Suserid: api.EncodeSessionUserID(session), Token: token, - Version: api.version, + Version: api.Version, Turn: api.CreateTurnData(session), Stun: api.StunURIs, } diff --git a/src/app/spreed-webrtc-server/channelling_api_test.go b/src/app/spreed-webrtc-server/channelling_api_test.go index c6a5e6dc..0f0f0241 100644 --- a/src/app/spreed-webrtc-server/channelling_api_test.go +++ b/src/app/spreed-webrtc-server/channelling_api_test.go @@ -26,10 +26,6 @@ import ( "testing" ) -const ( - testAppVersion string = "0.0.0+unittests" -) - type fakeClient struct { replies map[string]interface{} } @@ -103,7 +99,7 @@ func assertErrorReply(t *testing.T, client *fakeClient, iid, code string) { func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomManager) { 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) { diff --git a/src/app/spreed-webrtc-server/config.go b/src/app/spreed-webrtc-server/config.go index 82d06eba..588c066d 100644 --- a/src/app/spreed-webrtc-server/config.go +++ b/src/app/spreed-webrtc-server/config.go @@ -24,6 +24,10 @@ package main import ( "fmt" "net/http" + "strings" + "time" + + "github.com/strukturag/phoenix" ) type Config struct { @@ -41,30 +45,77 @@ type Config struct { UsersMode string // Users mode string DefaultRoomEnabled bool // Flag if default room ("") is enabled 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) } -func NewConfig(title, ver, runtimeVersion, basePath, serverToken string, stunURIs, turnURIs []string, tokens bool, globalRoomid string, defaultRoomEnabled, usersEnabled, usersAllowRegistration bool, usersMode, plugin string) *Config { - sv := fmt.Sprintf("static/ver=%s", ver) +func NewConfig(container phoenix.Container, tokens bool) *Config { + 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{ - Title: title, + Title: container.GetStringDefault("app", "title", "Spreed WebRTC"), ver: ver, - S: sv, + S: fmt.Sprintf("static/ver=%s", ver), B: basePath, Token: serverToken, StunURIs: stunURIs, TurnURIs: turnURIs, Tokens: tokens, - Version: runtimeVersion, - UsersEnabled: usersEnabled, - UsersAllowRegistration: usersAllowRegistration, - UsersMode: usersMode, - DefaultRoomEnabled: defaultRoomEnabled, - Plugin: plugin, - globalRoomid: globalRoomid, + Version: version, + UsersEnabled: container.GetBoolDefault("users", "enabled", false), + UsersAllowRegistration: container.GetBoolDefault("users", "allowRegistration", false), + UsersMode: container.GetStringDefault("users", "mode", ""), + DefaultRoomEnabled: container.GetBoolDefault("app", "defaultRoomEnabled", true), + Plugin: container.GetStringDefault("app", "plugin", ""), + globalRoomID: container.GetStringDefault("app", "globalRoom", ""), } } func (config *Config) Get(request *http.Request) (int, interface{}, http.Header) { 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] +} diff --git a/src/app/spreed-webrtc-server/main.go b/src/app/spreed-webrtc-server/main.go index 5bcbffb2..36273a10 100644 --- a/src/app/spreed-webrtc-server/main.go +++ b/src/app/spreed-webrtc-server/main.go @@ -40,7 +40,6 @@ import ( "path" goruntime "runtime" "strconv" - "strings" "syscall" "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) { 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?") } - // 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") if err != nil { 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 turnSecretString, err := runtime.GetString("app", "turnSecret") if err == nil { turnSecret = []byte(turnSecretString) } - stunURIsString, err := runtime.GetString("app", "stunURIs") - 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 { - plugin = "" - } - - 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 - usersAllowRegistrationString, err := runtime.GetString("users", "allowRegistration") - if err == nil { - usersAllowRegistration = usersAllowRegistrationString == "true" - } - - 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. + 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) + } + } + var tokenProvider TokenProvider if tokenFile != "" { log.Printf("Using token authorization from %s\n", tokenFile) tokenProvider = TokenFileProvider(tokenFile) } - // Create configuration data structure. - config = NewConfig(title, ver, runtimeVersion, basePath, serverToken, stunURIs, turnURIs, tokenProvider != nil, globalRoomid, defaultRoomEnabled, usersEnabled, usersAllowRegistration, usersMode, plugin) + // Load remaining configuration items. + config = NewConfig(runtime, tokenProvider != nil) // Load templates. tt := template.New("") @@ -373,7 +270,7 @@ func runner(runtime phoenix.Runtime) error { } // 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 if goruntime.GOMAXPROCS(0) == 1 { @@ -406,7 +303,7 @@ func runner(runtime phoenix.Runtime) error { // Create router. router := mux.NewRouter() - r := router.PathPrefix(basePath).Subrouter().StrictSlash(true) + r := router.PathPrefix(config.B).Subrouter().StrictSlash(true) // HTTP listener support. if _, err = runtime.GetString("http", "listen"); err == nil { @@ -434,12 +331,12 @@ func runner(runtime phoenix.Runtime) error { tickets := NewTickets(sessionSecret, encryptionSecret, computedRealm) sessionManager := NewSessionManager(config, tickets, sessionSecret) 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.Handle("/static/img/buddy/{flags}/{imageid}/{idx:.*}", http.StripPrefix(basePath, makeImageHandler(buddyImages, time.Duration(24)*time.Hour))) - r.Handle("/static/{path:.*}", http.StripPrefix(basePath, httputils.FileStaticServer(http.Dir(rootFolder)))) - r.Handle("/robots.txt", http.StripPrefix(basePath, 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("/static/img/buddy/{flags}/{imageid}/{idx:.*}", http.StripPrefix(config.B, makeImageHandler(buddyImages, time.Duration(24)*time.Hour))) + r.Handle("/static/{path:.*}", http.StripPrefix(config.B, httputils.FileStaticServer(http.Dir(rootFolder)))) + r.Handle("/robots.txt", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static"))))) + 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.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler)) @@ -449,11 +346,11 @@ func runner(runtime phoenix.Runtime) error { api.AddResource(&Rooms{}, "/rooms") api.AddResource(config, "/config") api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens") - if usersEnabled { + if config.UsersEnabled { // 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}/") - if usersAllowRegistration { + if config.UsersAllowRegistration { api.AddResource(users, "/users") } } @@ -466,7 +363,7 @@ func runner(runtime phoenix.Runtime) error { if extraFolder != "" { extraFolderStatic := path.Join(extraFolder, "static") 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) } } @@ -491,7 +388,7 @@ func boot() error { return nil } - return phoenix.NewServer("server", ""). + return phoenix.NewServer("server", version). Config(configPath). Log(logPath). CpuProfile(cpuprofile). diff --git a/src/app/spreed-webrtc-server/room_manager.go b/src/app/spreed-webrtc-server/room_manager.go index 9efc5420..f14c364e 100644 --- a/src/app/spreed-webrtc-server/room_manager.go +++ b/src/app/spreed-webrtc-server/room_manager.go @@ -49,18 +49,16 @@ type RoomManager interface { type roomManager struct { sync.RWMutex + *Config OutgoingEncoder - defaultRoomEnabled bool - globalRoomID string - roomTable map[string]RoomWorker + roomTable map[string]RoomWorker } func NewRoomManager(config *Config, encoder OutgoingEncoder) RoomManager { return &roomManager{ sync.RWMutex{}, + config, encoder, - config.DefaultRoomEnabled, - config.globalRoomid, make(map[string]RoomWorker), } } @@ -74,7 +72,7 @@ func (rooms *roomManager) RoomUsers(session *Session) []*DataSession { } 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"} } diff --git a/src/app/spreed-webrtc-server/roomworker_test.go b/src/app/spreed-webrtc-server/roomworker_test.go index ad785440..f963c5cd 100644 --- a/src/app/spreed-webrtc-server/roomworker_test.go +++ b/src/app/spreed-webrtc-server/roomworker_test.go @@ -30,14 +30,14 @@ const ( ) func NewTestRoomWorker() RoomWorker { - worker := NewRoomWorker(&roomManager{}, testRoomName, nil) + worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomName, nil) go worker.Start() return worker } func NewTestRoomWorkerWithPIN(t *testing.T) (RoomWorker, string) { pin := "asdf" - worker := NewRoomWorker(&roomManager{}, testRoomName, &DataRoomCredentials{PIN: pin}) + worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomName, &DataRoomCredentials{PIN: pin}) go worker.Start() return worker, pin } From 1f09f72bd2b13c1545f43bac20fc05ab2bfba3fd Mon Sep 17 00:00:00 2001 From: Lance Cooper Date: Fri, 14 Nov 2014 13:32:40 -0600 Subject: [PATCH 2/2] Server side support for requiring a user account to create rooms. Also streamlines error creations. --- doc/CHANNELING-API.txt | 2 + server.conf.in | 3 + src/app/spreed-webrtc-server/channeling.go | 4 ++ .../channelling_api_test.go | 4 +- src/app/spreed-webrtc-server/config.go | 2 + src/app/spreed-webrtc-server/room_manager.go | 65 +++++++++++-------- .../spreed-webrtc-server/room_manager_test.go | 32 +++++++-- src/app/spreed-webrtc-server/roomworker.go | 6 +- src/app/spreed-webrtc-server/session.go | 7 ++ 9 files changed, 88 insertions(+), 37 deletions(-) diff --git a/doc/CHANNELING-API.txt b/doc/CHANNELING-API.txt index 5a0ac709..4427ec7a 100644 --- a/doc/CHANNELING-API.txt +++ b/doc/CHANNELING-API.txt @@ -193,6 +193,8 @@ Special purpose documents for channling authorization_not_required : No credentials should be provided for this room. invalid_credentials : The provided credentials are incorrect. + room_join_requires_account : Server configuration requires an + authenticated user account to join this room. Welcome diff --git a/server.conf.in b/server.conf.in index 6b1c6acf..282c85da 100644 --- a/server.conf.in +++ b/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 ; form will be shown instead. ;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 generated security tokens. When the serverToken is changed all existing ; nonces become invalid. Use 32 or 64 characters (eg. 16 or 32 byte hex). diff --git a/src/app/spreed-webrtc-server/channeling.go b/src/app/spreed-webrtc-server/channeling.go index 7d3c516b..5099b114 100644 --- a/src/app/spreed-webrtc-server/channeling.go +++ b/src/app/spreed-webrtc-server/channeling.go @@ -27,6 +27,10 @@ type DataError struct { Message string } +func NewDataError(code, message string) error { + return &DataError{"Error", code, message} +} + func (err *DataError) Error() string { return err.Message } diff --git a/src/app/spreed-webrtc-server/channelling_api_test.go b/src/app/spreed-webrtc-server/channelling_api_test.go index 0f0f0241..0ee186c1 100644 --- a/src/app/spreed-webrtc-server/channelling_api_test.go +++ b/src/app/spreed-webrtc-server/channelling_api_test.go @@ -195,7 +195,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) { iid := "foo" 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{}}) @@ -231,7 +231,7 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) { iid, roomName := "123", "foo" 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: "Room", Iid: iid, Room: &DataRoom{Name: roomName}}) diff --git a/src/app/spreed-webrtc-server/config.go b/src/app/spreed-webrtc-server/config.go index 588c066d..2a727e3b 100644 --- a/src/app/spreed-webrtc-server/config.go +++ b/src/app/spreed-webrtc-server/config.go @@ -46,6 +46,7 @@ type Config struct { DefaultRoomEnabled bool // Flag if default room ("") is enabled Plugin string // Plugin to load 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(container phoenix.Container, tokens bool) *Config { @@ -98,6 +99,7 @@ func NewConfig(container phoenix.Container, tokens bool) *Config { DefaultRoomEnabled: container.GetBoolDefault("app", "defaultRoomEnabled", true), Plugin: container.GetStringDefault("app", "plugin", ""), globalRoomID: container.GetStringDefault("app", "globalRoom", ""), + authorizeRoomCreation: container.GetBoolDefault("app", "authorizeRoomCreation", false), } } diff --git a/src/app/spreed-webrtc-server/room_manager.go b/src/app/spreed-webrtc-server/room_manager.go index f14c364e..f0d40c3c 100644 --- a/src/app/spreed-webrtc-server/room_manager.go +++ b/src/app/spreed-webrtc-server/room_manager.go @@ -73,10 +73,15 @@ func (rooms *roomManager) RoomUsers(session *Session) []*DataSession { func (rooms *roomManager) JoinRoom(id string, credentials *DataRoomCredentials, session *Session, sender Sender) (*DataRoom, error) { 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) { @@ -87,7 +92,7 @@ func (rooms *roomManager) LeaveRoom(session *Session) { func (rooms *roomManager) UpdateRoom(session *Session, room *DataRoom) (*DataRoom, error) { 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 // correctly, however clients cannot not handle it currently. @@ -147,32 +152,38 @@ func (rooms *roomManager) Get(id string) (room RoomWorker, ok bool) { return } -func (rooms *roomManager) GetOrCreate(id string, credentials *DataRoomCredentials) RoomWorker { - room, ok := rooms.Get(id) - if !ok { - rooms.Lock() - // Need to re-check, another thread might have created the room - // while we waited for the lock. - room, ok = rooms.roomTable[id] - if !ok { - room = NewRoomWorker(rooms, id, credentials) - rooms.roomTable[id] = room - rooms.Unlock() - go func() { - // Start room, this blocks until room expired. - room.Start() - // Cleanup room when we are done. - rooms.Lock() - defer rooms.Unlock() - delete(rooms.roomTable, id) - log.Printf("Cleaned up room '%s'\n", id) - }() - } else { - rooms.Unlock() - } +func (rooms *roomManager) GetOrCreate(id string, credentials *DataRoomCredentials, session *Session) (RoomWorker, error) { + if room, ok := rooms.Get(id); ok { + return room, nil + } + + rooms.Lock() + // Need to re-check, another thread might have created the room + // while we waited for the lock. + if room, ok := rooms.roomTable[id]; ok { + rooms.Unlock() + 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") } - return room + room := NewRoomWorker(rooms, id, credentials) + rooms.roomTable[id] = room + rooms.Unlock() + go func() { + // Start room, this blocks until room expired. + room.Start() + // Cleanup room when we are done. + rooms.Lock() + defer rooms.Unlock() + delete(rooms.roomTable, id) + log.Printf("Cleaned up room '%s'\n", id) + }() + + return room, nil } func (rooms *roomManager) GlobalUsers() []*roomUser { diff --git a/src/app/spreed-webrtc-server/room_manager_test.go b/src/app/spreed-webrtc-server/room_manager_test.go index 55ceb2e4..571097b4 100644 --- a/src/app/spreed-webrtc-server/room_manager_test.go +++ b/src/app/spreed-webrtc-server/room_manager_test.go @@ -25,26 +25,48 @@ import ( "testing" ) -func NewTestRoomManager() RoomManager { - return NewRoomManager(&Config{}, nil) +func NewTestRoomManager() (RoomManager, *Config) { + 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) { - roomManager := NewTestRoomManager() + roomManager, _ := NewTestRoomManager() _, err := roomManager.UpdateRoom(&Session{}, nil) assertDataError(t, err, "not_in_room") } func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfUpdatingAnUnjoinedRoom(t *testing.T) { - roomManager := NewTestRoomManager() + roomManager, _ := NewTestRoomManager() session := &Session{Hello: true, Roomid: "foo"} _, err := roomManager.UpdateRoom(session, &DataRoom{Name: "bar"}) assertDataError(t, err, "not_in_room") } func Test_RoomManager_UpdateRoom_ReturnsACorrectlyTypedDocument(t *testing.T) { - roomManager := NewTestRoomManager() + roomManager, _ := NewTestRoomManager() session := &Session{Hello: true, Roomid: "foo"} room, err := roomManager.UpdateRoom(session, &DataRoom{Name: session.Roomid}) if err != nil { diff --git a/src/app/spreed-webrtc-server/roomworker.go b/src/app/spreed-webrtc-server/roomworker.go index 2e52fefa..849ac436 100644 --- a/src/app/spreed-webrtc-server/roomworker.go +++ b/src/app/spreed-webrtc-server/roomworker.go @@ -244,18 +244,18 @@ func (r *roomWorker) Join(credentials *DataRoomCredentials, session *Session, se worker := func() { r.mutex.Lock() 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() return } else if r.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() return } 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() return } diff --git a/src/app/spreed-webrtc-server/session.go b/src/app/spreed-webrtc-server/session.go index 92623b16..905acd72 100644 --- a/src/app/spreed-webrtc-server/session.go +++ b/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 { s.mutex.Lock()