Browse Source

Server side support for requiring a user account to create rooms.

Also streamlines error creations.
pull/148/head
Lance Cooper 11 years ago
parent
commit
1f09f72bd2
  1. 2
      doc/CHANNELING-API.txt
  2. 3
      server.conf.in
  3. 4
      src/app/spreed-webrtc-server/channeling.go
  4. 4
      src/app/spreed-webrtc-server/channelling_api_test.go
  5. 2
      src/app/spreed-webrtc-server/config.go
  6. 65
      src/app/spreed-webrtc-server/room_manager.go
  7. 32
      src/app/spreed-webrtc-server/room_manager_test.go
  8. 6
      src/app/spreed-webrtc-server/roomworker.go
  9. 7
      src/app/spreed-webrtc-server/session.go

2
doc/CHANNELING-API.txt

@ -193,6 +193,8 @@ Special purpose documents for channling @@ -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

3
server.conf.in

@ -78,6 +78,9 @@ encryptionSecret = tne-default-encryption-block-key @@ -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).

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

@ -27,6 +27,10 @@ type DataError struct { @@ -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
}

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

@ -195,7 +195,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t @@ -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 @@ -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}})

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

@ -46,6 +46,7 @@ type Config struct { @@ -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 { @@ -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),
}
}

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

@ -73,10 +73,15 @@ func (rooms *roomManager) RoomUsers(session *Session) []*DataSession { @@ -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) { @@ -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) { @@ -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 {

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

@ -25,26 +25,48 @@ import ( @@ -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 {

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

@ -244,18 +244,18 @@ func (r *roomWorker) Join(credentials *DataRoomCredentials, session *Session, se @@ -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
}

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

@ -173,6 +173,13 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) { @@ -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()

Loading…
Cancel
Save