Created protocol exchanges for creating controlled rooms
This commit is contained in:
parent
da633e2883
commit
cdce17b10b
@ -417,38 +417,30 @@ class SyncplayClient(object):
|
||||
|
||||
def createControlledRoom(self):
|
||||
controlPassword = RoomPasswordGenerator.generate_password()
|
||||
# TODO (Client): Send request to server; handle success and failure
|
||||
# TODO (Server): Process request, send response
|
||||
self.ui.showMessage("Attempting to create controlled room suffix with password '{}'...".format(controlPassword))
|
||||
self._protocol.requestControlledRoom(controlPassword)
|
||||
|
||||
def controlledRoomCreated(self, controlPassword, roomName):
|
||||
# NOTE (Client): Triggered by protocol to handle createControlledRoom when room is created
|
||||
self.ui.showMessage("Created controlled room suffix '{}' with password '{}'. Please save this information for future reference!".format(roomName, controlPassword))
|
||||
self.setRoom(roomName)
|
||||
self.sendRoom()
|
||||
self._protocol.requestControlledRoom(controlPassword)
|
||||
self.ui.updateRoomName(roomName)
|
||||
|
||||
def controlledRoomCreationError(self, errormsg):
|
||||
# NOTE (Client): Triggered by protocol to handle createControlledRoom if controlled rooms are not supported by server or if password is malformed
|
||||
# NOTE (Server): Triggered by protocol to handle createControlledRoom if password is malformed
|
||||
self.ui.showErrorMessage("Failed to create the controlled room suffix for the following reason: {}.".format(errormsg))
|
||||
|
||||
def identifyAsController(self, controlPassword):
|
||||
# TODO (Client): Send identification to server; handle success and failure
|
||||
# TODO (Server): Process request, send response
|
||||
self.ui.showMessage("Identifying as room controller with password '{}'...".format(controlPassword))
|
||||
self._protocol.requestControlledRoom(controlPassword)
|
||||
|
||||
def controllerIdentificationError(self, errormsg):
|
||||
# NOTE (Client): Triggered by protocol handling identiedAsController, e.g. on server response or not supported error
|
||||
# NOTE (Server): Relevant error given in response to identifyAsController if password is wrong
|
||||
self.ui.showErrorMessage("Failed to identify as a room controller for the following reason: {}.".format(errormsg))
|
||||
def controllerIdentificationError(self):
|
||||
self.ui.showErrorMessage("Failed to identify as a room controller.")
|
||||
|
||||
def notControllerError(self, errormsg):
|
||||
# NOTE (Client): Trigger when client gets a "not controller" error from server (e.g. due to illegal pauses, unpauses and seeks)
|
||||
# NOTE (Server): Give "not controller" error when users try to perform illegal pause, unpause or seek
|
||||
self.ui.showErrorMessage("There are currently people with 'room controller' status in this room. As such, only they can pause, unpause and seek. If you want to perform these actions then you must either identify as a controller or join a different room. See http://syncplay.pl/guide/ for more details.")
|
||||
def controllerIdentificationSuccess(self):
|
||||
# TODO: More UI stuff
|
||||
self.ui.showErrorMessage("Authenticated as a room controller")
|
||||
|
||||
# TODO: A person authenticated as a room controller
|
||||
# TODO: Disable UI's "Create new Controlled Room when in Controlled Room"
|
||||
# TODO: Disable authenticate when authenticated
|
||||
|
||||
class _WarningManager(object):
|
||||
def __init__(self, player, userlist, ui):
|
||||
|
||||
@ -114,13 +114,21 @@ class SyncClientProtocol(JSONCommandProtocol):
|
||||
self._client.userlist.modUser(username, room, file_)
|
||||
|
||||
def handleSet(self, settings):
|
||||
for set_ in settings.iteritems():
|
||||
command = set_[0]
|
||||
for (command, values) in settings.iteritems():
|
||||
if command == "room":
|
||||
roomName = set_[1]["name"] if set_[1].has_key("name") else None
|
||||
roomName = values["name"] if values.has_key("name") else None
|
||||
self._client.setRoom(roomName)
|
||||
elif command == "user":
|
||||
self._SetUser(set_[1])
|
||||
self._SetUser(values)
|
||||
elif command == "controllerAuth":
|
||||
if values['success']:
|
||||
self._client.controllerIdentificationSuccess()
|
||||
else:
|
||||
self._client.controllerIdentificationError()
|
||||
elif command == "newControlledRoom":
|
||||
controlPassword = values['password']
|
||||
roomName = values['roomName']
|
||||
self._client.controlledRoomCreated(controlPassword, roomName)
|
||||
|
||||
def sendSet(self, setting):
|
||||
self.sendMessage({"Set": setting})
|
||||
@ -210,8 +218,15 @@ class SyncClientProtocol(JSONCommandProtocol):
|
||||
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
|
||||
self.sendMessage({"State": state})
|
||||
|
||||
def requestControlledRoom(self, password):
|
||||
self.sendSet({
|
||||
"controllerAuth": {
|
||||
"password": password
|
||||
}
|
||||
})
|
||||
|
||||
def handleError(self, error):
|
||||
self.dropWithError(error["message"]) # TODO: more processing and fallbacking
|
||||
self.dropWithError(error["message"])
|
||||
|
||||
def sendError(self, message):
|
||||
self.sendMessage({"Error": {"message": message}})
|
||||
@ -311,10 +326,28 @@ class SyncServerProtocol(JSONCommandProtocol):
|
||||
self._factory.setWatcherRoom(self._watcher, roomName)
|
||||
elif command == "file":
|
||||
self._watcher.setFile(set_[1])
|
||||
elif command == "controllerAuth":
|
||||
password = set_[1]["password"] if set_[1].has_key("password") else None
|
||||
self._factory.authRoomController(self._watcher, password)
|
||||
|
||||
def sendSet(self, setting):
|
||||
self.sendMessage({"Set": setting})
|
||||
|
||||
def sendNewControlledRoom(self, roomName, password):
|
||||
self.sendSet({
|
||||
"newControlledRoom": {
|
||||
"password": password,
|
||||
"roomName": roomName
|
||||
}
|
||||
})
|
||||
|
||||
def sendControlledRoomAuthStatus(self, success):
|
||||
self.sendSet({
|
||||
"controllerAuth": {
|
||||
"success": success
|
||||
}
|
||||
})
|
||||
|
||||
def sendUserSetting(self, username, room, file_, event):
|
||||
room = {"name": room.getName()}
|
||||
user = {}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import hashlib
|
||||
import random
|
||||
import re
|
||||
from twisted.internet import task, reactor
|
||||
from twisted.internet.protocol import Factory
|
||||
import syncplay
|
||||
@ -11,12 +13,18 @@ import os
|
||||
from string import Template
|
||||
import argparse
|
||||
|
||||
# TODO: Check if room should have status of "Controlled room"
|
||||
# TODO: Make only controllers able to control a room
|
||||
# TODO: Send list of controllers
|
||||
# TODO: Broadcast information about controller auth
|
||||
class SyncFactory(Factory):
|
||||
def __init__(self, password='', motdFilePath=None, isolateRooms=False):
|
||||
print getMessage("welcome-server-notification").format(syncplay.version)
|
||||
if password:
|
||||
password = hashlib.md5(password).hexdigest()
|
||||
self.password = password
|
||||
# TODO: Make salt come from more reasonable place
|
||||
self._salt = str(random.random())
|
||||
self._motdFilePath = motdFilePath
|
||||
if not isolateRooms:
|
||||
self._roomManager = RoomManager()
|
||||
@ -98,6 +106,19 @@ class SyncFactory(Factory):
|
||||
def getAllWatchersForUser(self, forUser):
|
||||
return self._roomManager.getAllWatchersForUser(forUser)
|
||||
|
||||
def authRoomController(self, watcher, password):
|
||||
room = watcher.getRoom()
|
||||
try:
|
||||
success = RoomPasswordProvider.check(room.getName(), password, self._salt)
|
||||
# TODO: Authenticate watcher to make changes in the room
|
||||
watcher.sendControlledRoomAuthStatus(success)
|
||||
except NotControlledRoom:
|
||||
newName = RoomPasswordProvider.getControlledRoomName(room.getName(), password, self._salt)
|
||||
watcher.sendNewControlledRoom(newName, password)
|
||||
except ValueError:
|
||||
watcher.sendControlledRoomAuthStatus(False)
|
||||
|
||||
|
||||
class RoomManager(object):
|
||||
def __init__(self):
|
||||
self._rooms = {}
|
||||
@ -277,6 +298,12 @@ class Watcher(object):
|
||||
def sendSetting(self, user, room, file_, event):
|
||||
self._connector.sendUserSetting(user, room, file_, event)
|
||||
|
||||
def sendNewControlledRoom(self, roomName, password):
|
||||
self._connector.sendNewControlledRoom(roomName, password)
|
||||
|
||||
def sendControlledRoomAuthStatus(self, success):
|
||||
self._connector.sendControlledRoomAuthStatus(success)
|
||||
|
||||
def __lt__(self, b):
|
||||
if self.getPosition() is None or self._file is None:
|
||||
return False
|
||||
@ -341,3 +368,33 @@ class ConfigurationGetter(object):
|
||||
self._argparser.add_argument('--password', metavar='password', type=str, nargs='?', help=getMessage("server-password-argument"))
|
||||
self._argparser.add_argument('--isolate-rooms', action='store_true', help=getMessage("server-isolate-room-argument"))
|
||||
self._argparser.add_argument('--motd-file', metavar='file', type=str, nargs='?', help=getMessage("server-motd-argument"))
|
||||
|
||||
|
||||
class RoomPasswordProvider(object):
|
||||
CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$")
|
||||
PASSWORD_REGEX = re.compile("[A-Z]{2}-\d{3}-\d{3}")
|
||||
|
||||
@staticmethod
|
||||
def check(roomName, password, salt):
|
||||
if not re.match(RoomPasswordProvider.PASSWORD_REGEX, password):
|
||||
raise ValueError()
|
||||
|
||||
match = re.match(RoomPasswordProvider.CONTROLLED_ROOM_REGEX, roomName)
|
||||
if not match:
|
||||
raise NotControlledRoom()
|
||||
roomHash = match.group(2)
|
||||
computedHash = RoomPasswordProvider._computeRoomHash(match.group(1), password, salt)
|
||||
return roomHash == computedHash
|
||||
|
||||
@staticmethod
|
||||
def getControlledRoomName(roomName, password, salt):
|
||||
return "+" + roomName + ":" + RoomPasswordProvider._computeRoomHash(roomName, password, salt)
|
||||
|
||||
@staticmethod
|
||||
def _computeRoomHash(roomName, password, salt):
|
||||
salt = hashlib.sha256(salt).hexdigest()
|
||||
provisionalHash = hashlib.sha256(roomName + salt).hexdigest()
|
||||
return hashlib.sha1(provisionalHash + salt + password).hexdigest()[:12].upper()
|
||||
|
||||
class NotControlledRoom(Exception):
|
||||
pass
|
||||
Loading…
x
Reference in New Issue
Block a user