From: <umg...@us...> - 2007-02-28 18:21:38
|
Revision: 355 http://svn.sourceforge.net/pybridge/?rev=355&view=rev Author: umgangee Date: 2007-02-28 10:21:26 -0800 (Wed, 28 Feb 2007) Log Message: ----------- New SQLObject object-relational database model, replacing flat-file user account store. Modified Paths: -------------- trunk/pybridge/pybridge/server/checker.py trunk/pybridge/pybridge/server/database.py trunk/pybridge/pybridge/server/server.py trunk/pybridge/pybridge/server/user.py Modified: trunk/pybridge/pybridge/server/checker.py =================================================================== --- trunk/pybridge/pybridge/server/checker.py 2007-02-28 18:11:30 UTC (rev 354) +++ trunk/pybridge/pybridge/server/checker.py 2007-02-28 18:21:26 UTC (rev 355) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,7 +10,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -21,7 +21,7 @@ from twisted.python import failure, log from zope.interface import implements -from database import database +import database as db class Checker: @@ -34,35 +34,33 @@ def __init__(self): - self.database = database self.users = {} # Users online, from Server object. def requestAvatarId(self, credentials): - - def gotUser(user): - password = user.get('password', '') - d = defer.maybeDeferred(credentials.checkPassword, password) - d.addCallback(passwordMatch) - return d - + + def unauthorized(reason): + log.msg("Login failed for %s: %s" % (credentials.username, reason)) + return failure.Failure(error.UnauthorizedLogin(reason)) + def passwordMatch(matched): if matched: - if credentials.username in self.users.keys(): - raise unauthorized('Already logged in') + if credentials.username in self.users: + # TODO: delete old session and use this one instead? + return unauthorized("User is already logged in") else: return credentials.username else: - return unauthorized('Incorrect password') - - def unauthorized(reason): - log.msg('Login failed for %s: %s' % (credentials.username, reason)) - return failure.Failure(error.UnauthorizedLogin(reason)) - + return unauthorized("Incorrect password for user") + if credentials.username == '': - return checkers.ANONYMOUS - else: - d = self.database.getUser(credentials.username) - d.addCallbacks(gotUser, lambda e: unauthorized('No user account')) - return d + return checkers.ANONYMOUS # TODO: if allowAnonymousRegistration. + users = db.UserAccount.selectBy(username=credentials.username) + if users.count() is 0: + return unauthorized("User not known on server") + + d = defer.maybeDeferred(credentials.checkPassword, users[0].password) + d.addCallback(passwordMatch) + return d + Modified: trunk/pybridge/pybridge/server/database.py =================================================================== --- trunk/pybridge/pybridge/server/database.py 2007-02-28 18:11:30 UTC (rev 354) +++ trunk/pybridge/pybridge/server/database.py 2007-02-28 18:21:26 UTC (rev 355) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,81 +10,141 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import shelve, anydbm, dbhash -from twisted.internet import defer -from twisted.python import failure +import os, re +from datetime import datetime +from sqlobject import * +from sqlobject.inheritance import InheritableSQLObject -from pybridge.environment import environment +import pybridge.environment as env +from pybridge.bridge.deck import Seat -class DuplicateError(Exception): - pass +backend = "sqlite" -class UnknownError(Exception): - pass +# Initiate connection to the appropriate database backend. +if backend == "sqlite": + db_filename = env.find_config_server("pybridge-server.db") + connection_string = "sqlite://" + db_filename # TODO: fix for Win32. +connection = connectionForURI(connection_string) +sqlhub.processConnection = connection # Set all classes to use connection. -class UserDatabase: - """A simple database of user accounts.""" +class UserAccount(SQLObject): + """A store of user information. + + A user account is created when a user is registered. + """ - def __init__(self): - # Open the database file. - dbfile = environment.find_configfile('users.db') - self.accounts = shelve.open(dbfile, 'c', writeback=True) + username = StringCol(length=20, notNone=True, unique=True, alternateID=True) + password = StringCol(length=40, notNone=True) # Store SHA-1 hex hashes. + allowLogin = BoolCol(default=True) # If False, account login is disabled. + email = StringCol(default=None, length=320) # See RFC 2821 section 4.5.3.1. + realname = UnicodeCol(default=None, length=40) + profile = UnicodeCol(default=None) + created = DateTimeCol(default=datetime.now) + lastLogin = DateTimeCol(default=None) + # friends = MultipleJoin('UserFriend', joinColumn='from_user') + def _set_username(self, value): + if not isinstance(value, str) or not(1 <= len(value) <= 20): + raise ValueError, "Invalid specification of username" + if re.search("[^A-z0-9_]", value): + raise ValueError, "Username can only contain alphanumeric characters" + self._SO_set_username(value) - def addUser(self, username, **attrs): - """Adds a new user.""" - if self.accounts.has_key(username): - f = failure.Failure(DuplicateError()) - return defer.fail(f) - - profile = attrs.copy() - profile['username'] = username - self.accounts[username] = profile - - return defer.succeed(username) + def _set_password(self, value): + if not isinstance(value, str) or not(1 <= len(value) <= 40): + raise ValueError, "Invalid specification of password" + self._SO_set_password(value) + def _set_email(self, value): + # This regexp matches virtually all well-formatted email addresses. + if value and not re.match("^[A-z0-9_.+-]+@([A-z0-9-]+\.)+[A-z]{2,6}$", value): + raise ValueError, "Invalid or ill-formatted email address" + self._SO_set_email(value) - def removeUser(self, username): - """Removes an existing user.""" - if not self.accounts.has_key(username): - f = failure.Failure(UnknownError()) - return defer.fail(f) - - del self.accounts[username] - - return defer.succeed(username) - - def updateUser(self, username, **attrs): - """Updates attributes for an existing user.""" - if not self.accounts.has_key(username): - f = failure.Failure(UnknownError()) - return defer.fail(f) - - self.accounts[username].update(attrs) - - return defer.succeed(username) +for table in [UserAccount]: + table.createTable(ifNotExists=True) - def getUser(self, username): - """Returns a dict of information for an existing user.""" - if not self.accounts.has_key(username): - f = failure.Failure(UnknownError()) - return defer.fail(f) - - info = self.accounts[username] - - return defer.succeed(info) +# The following tables are not used by PyBridge 0.3. +# They will be enhanced and used in future releases. -database = UserDatabase() +class UserFriend(SQLObject): + """Models the social interconnections that exist between users. + + Client software may use this information to provide visual clues to users + that members of their "social circle" are online. + + Users may specify the nature of their relationships: this takes inspiration + from the XFN (XHTML Friend Network) model: see http://gmpg.org/xfn/. The + symmetry arising from some types of relationship is eschewed for simplicity. + + This relation is irreflexive: no user can form a friendship with themselves! + """ + fromUser = ForeignKey('UserAccount') # The creator of the relationship. + toUser = ForeignKey('UserAccount') # The subject of the relationship. + fromToIndex = DatabaseIndex('fromUser', 'toUser', unique=True) + + # XFN attributes. + friendship = EnumCol(default=None, enumValues=['friend', 'acquaintance', 'contact']) + physical = BoolCol(default=False) # Having met in person. + professional = EnumCol(default=None, enumValues=['co-worker', 'colleague']) + geographical = EnumCol(default=None, enumValues=['co-resident', 'neighbour']) + family = EnumCol(default=None, enumValues=['child', 'parent', 'sibling', 'spouse', 'kin']) + romantic = EnumCol(default=None, enumValues=['muse', 'crush', 'date', 'sweetheart']) + + +class Game(InheritableSQLObject): + """Captures game attributes common to all games. + + Implementations of specific games should inherit from this class. + """ + + start = DateTimeCol() + complete = DateTimeCol() + + +class BridgeGame(Game): + """Captures game attributes specific to bridge games. + + """ + + board = ForeignKey('BridgeBoard') + + declarer = EnumCol(enumValues=list(Seat)) +# contract = + trickCount = IntCol() # Number of tricks won by + score = IntCol() + + # Although key attributes of games are stored in fields (for searching), + # the complete game is represented in PBN format. + pbn = StringCol() + + # Players: no player may occupy more than one position. + north = ForeignKey('UserAccount') + east = ForeignKey('UserAccount') + south = ForeignKey('UserAccount') + west = ForeignKey('UserAccount') + + +class BridgeBoard(SQLObject): + """Encapsulates the attributes which may be common to multiple bridge games. + + Separating board attributes from . + """ + + deal = IntCol() + dealer = EnumCol(enumValues=list(Seat)) + vuln = EnumCol(enumValues=['none', 'ns', 'ew', 'all']) + Modified: trunk/pybridge/pybridge/server/server.py =================================================================== --- trunk/pybridge/pybridge/server/server.py 2007-02-28 18:11:30 UTC (rev 354) +++ trunk/pybridge/pybridge/server/server.py 2007-02-28 18:21:26 UTC (rev 355) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,16 +10,19 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from datetime import datetime from twisted.python import log -from database import database -import pybridge +import database as db +from pybridge import __version__ + +from pybridge.network.error import DeniedRequest, IllegalRequest from pybridge.network.tablemanager import LocalTableManager from pybridge.network.usermanager import LocalUserManager @@ -32,13 +35,14 @@ def __init__(self): self.tables = LocalTableManager() self.users = LocalUserManager() - self.version = pybridge.__version__ + self.version = __version__ self.supported = ['bridge'] def userConnects(self, user): """""" self.users.userLoggedIn(user) + db.UserAccount.byUsername(user.name).set(lastLogin=datetime.now()) log.msg("User %s connected" % user.name) @@ -51,11 +55,26 @@ # Methods invoked by user perspectives. - def userRegister(self, username, password): + def registerUser(self, username, password): + """Registers a new user account in the database. + + @param username: the unique username requested by user. + @param password: the password to be associated with the account. + """ + # Check that username has not already been registered. + if db.UserAccount.selectBy(username=username).count() > 0: + raise DeniedRequest, "Username already registered" + try: + # Create user account. + db.UserAccount(username=username, password=password, allowLogin=True) + log.msg("New user %s registered" % username) + except ValueError, err: + raise IllegalRequest, err + + + def userChangePassword(self, user, password): """""" - d = database.addUser(username, password=password) - log.msg("New user %s registered" % username) - return d + pass def createTable(self, tableid, tabletype): Modified: trunk/pybridge/pybridge/server/user.py =================================================================== --- trunk/pybridge/pybridge/server/user.py 2007-02-28 18:11:30 UTC (rev 354) +++ trunk/pybridge/pybridge/server/user.py 2007-02-28 18:21:26 UTC (rev 355) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,13 +10,15 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import re +#from twisted.internet import defer +#from twisted.python import failure from twisted.spread import pb from pybridge.network.error import DeniedRequest, IllegalRequest @@ -73,13 +75,13 @@ def perspective_hostTable(self, tableid, tabletype): """Creates a new table.""" if not isinstance(tableid, str): - raise IllegalRequest, 'Invalid parameter for table identifier' + raise IllegalRequest, "Invalid parameter for table identifier" elif not(0 < len(tableid) < 21) or re.search("[^A-Za-z0-9_ ]", tableid): - raise IllegalRequest, 'Invalid table identifier format' + raise IllegalRequest, "Invalid table identifier format" elif tableid in self.server.tables: - raise DeniedRequest, 'Table name exists' + raise DeniedRequest, "Table name exists" elif tabletype not in self.server.supported: - raise DeniedRequest, 'Table type not suppported by this server' + raise DeniedRequest, "Table type not suppported by this server" self.server.createTable(tableid, tabletype) return self.perspective_joinTable(tableid) # Force join to table. @@ -88,11 +90,11 @@ def perspective_joinTable(self, tableid): """Joins an existing table.""" if not isinstance(tableid, str): - raise IllegalRequest, 'Invalid parameter for table name' + raise IllegalRequest, "Invalid parameter for table name" elif tableid not in self.server.tables: - raise DeniedRequest, 'No such table' + raise DeniedRequest, "No such table" elif tableid in self.tables: - raise DeniedRequest, 'Already joined table' + raise DeniedRequest, "Already joined table" table = self.server.tables[tableid] self.tables[tableid] = table @@ -103,11 +105,11 @@ def perspective_leaveTable(self, tableid): """Leaves a table.""" if not isinstance(tableid, str): - raise IllegalRequest, 'Invalid parameter for table name' + raise IllegalRequest, "Invalid parameter for table name" elif tableid not in self.tables: - raise DeniedRequest, 'Not joined to table' + raise DeniedRequest, "Not joined to table" - del self.tables[tableid] + del self.tables[tableid] # Implicitly removes user from table. @@ -116,11 +118,7 @@ def perspective_register(self, username, password): - """Register a user account with given username and password.""" - if not isinstance(username, str): - raise IllegalRequest, 'Invalid parameter for user name' - elif not isinstance(password, str): - raise IllegalRequest, 'Invalid parameter for password' - - self.server.userRegister(username, password) + """Create a user account with specified username and password.""" + # TODO: consider defer.succeed, defer.fail, failure.Failure + self.server.registerUser(username, password) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-04-06 17:56:09
|
Revision: 401 http://svn.sourceforge.net/pybridge/?rev=401&view=rev Author: umgangee Date: 2007-04-06 10:55:41 -0700 (Fri, 06 Apr 2007) Log Message: ----------- Update references to roster classes. Modified Paths: -------------- trunk/pybridge/pybridge/server/server.py trunk/pybridge/pybridge/server/user.py Modified: trunk/pybridge/pybridge/server/server.py =================================================================== --- trunk/pybridge/pybridge/server/server.py 2007-04-06 17:54:58 UTC (rev 400) +++ trunk/pybridge/pybridge/server/server.py 2007-04-06 17:55:41 UTC (rev 401) @@ -23,34 +23,36 @@ from pybridge import __version__ from pybridge.network.error import DeniedRequest, IllegalRequest +from pybridge.network.localtable import LocalTable from pybridge.network.tablemanager import LocalTableManager from pybridge.network.usermanager import LocalUserManager -from pybridge.network.localtable import LocalTable from pybridge.bridge.game import BridgeGame -class Server: +class Server(object): def __init__(self): + # Set up rosters. self.tables = LocalTableManager() self.users = LocalUserManager() + self.version = __version__ self.supported = ['bridge'] def userConnects(self, user): """""" - self.users.userLoggedIn(user) + log.msg("User %s connected" % user.name) + self.users.userLogin(user) db.UserAccount.byUsername(user.name).set(lastLogin=datetime.now()) - log.msg("User %s connected" % user.name) def userDisconnects(self, user): """""" - self.users.userLoggedOut(user) log.msg("User %s disconnected" % user.name) + self.users.userLogout(user) # Methods invoked by user perspectives. @@ -85,4 +87,5 @@ table.id = tableid table.server = self self.tables.openTable(table) + #self.tables[tableid] = table Modified: trunk/pybridge/pybridge/server/user.py =================================================================== --- trunk/pybridge/pybridge/server/user.py 2007-04-06 17:54:58 UTC (rev 400) +++ trunk/pybridge/pybridge/server/user.py 2007-04-06 17:55:41 UTC (rev 401) @@ -26,7 +26,9 @@ class User(pb.Avatar): + info = property(lambda self: {}) + def __init__(self, name): self.name = name # User name. self.server = None # Set by Realm. @@ -62,16 +64,16 @@ return info - def perspective_getTables(self): - """Provides RemoteTableManager to the client.""" - return self.server.tables + def perspective_getRoster(self, name): + """Provides roster requested by client.""" + if name == 'tables': + return self.server.tables + elif name == 'users': + return self.server.users + else: + raise DeniedRequest, "Unknown roster name \'%s\'" % name - def perspective_getUsers(self): - """Provides RemoteUserManager to the client.""" - return self.server.users - - def perspective_hostTable(self, tableid, tabletype): """Creates a new table.""" if not isinstance(tableid, str): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <umg...@us...> - 2007-07-10 17:25:04
|
Revision: 471 http://svn.sourceforge.net/pybridge/?rev=471&view=rev Author: umgangee Date: 2007-07-10 10:25:05 -0700 (Tue, 10 Jul 2007) Log Message: ----------- Tidy up server stuff, particularly the server 'hub'. Modified Paths: -------------- trunk/pybridge/pybridge/server/__init__.py trunk/pybridge/pybridge/server/checker.py trunk/pybridge/pybridge/server/realm.py trunk/pybridge/pybridge/server/server.py trunk/pybridge/pybridge/server/user.py Modified: trunk/pybridge/pybridge/server/__init__.py =================================================================== --- trunk/pybridge/pybridge/server/__init__.py 2007-07-10 17:19:10 UTC (rev 470) +++ trunk/pybridge/pybridge/server/__init__.py 2007-07-10 17:25:05 UTC (rev 471) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,29 +10,22 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from twisted.cred import checkers, credentials, portal -from twisted.spread import pb - import config config.load() +from twisted.cred import portal +from twisted.spread import pb from pybridge.server.checker import Checker from pybridge.server.realm import Realm -from pybridge.server.server import Server -server = Server() realm = Realm() checker = Checker() - -realm.server = server -checker.users = server.users - p = portal.Portal(realm) p.registerChecker(checker) Modified: trunk/pybridge/pybridge/server/checker.py =================================================================== --- trunk/pybridge/pybridge/server/checker.py 2007-07-10 17:19:10 UTC (rev 470) +++ trunk/pybridge/pybridge/server/checker.py 2007-07-10 17:25:05 UTC (rev 471) @@ -22,9 +22,10 @@ from zope.interface import implements import database as db +import server -class Checker: +class Checker(object): """A database-driven implementation of ICredentialsChecker.""" implements(checkers.ICredentialsChecker) @@ -33,10 +34,6 @@ credentials.IUsernameHashedPassword) - def __init__(self): - self.users = {} # Users online, from Server object. - - def requestAvatarId(self, credentials): def unauthorized(reason): @@ -52,16 +49,16 @@ if credentials.username == '': return checkers.ANONYMOUS # TODO: if allowAnonymousRegistration. - users = db.UserAccount.selectBy(username=credentials.username) - if users.count() is 0: - return unauthorized("User not known on server") - elif users[0].allowLogin is False: + userQuery = db.UserAccount.selectBy(username=credentials.username) + if userQuery.count() == 0: + return unauthorized("User account does not exist on server") + elif userQuery[0].allowLogin is False: # TODO: list index breaks on MySQL. return unauthorized("User account is disabled") - elif credentials.username in self.users: + elif credentials.username in server.onlineUsers: # TODO: delete old session and use this one instead? return unauthorized("User is already logged in") - d = defer.maybeDeferred(credentials.checkPassword, users[0].password) + d = defer.maybeDeferred(credentials.checkPassword, userQuery[0].password) d.addCallback(passwordMatch) return d - + Modified: trunk/pybridge/pybridge/server/realm.py =================================================================== --- trunk/pybridge/pybridge/server/realm.py 2007-07-10 17:19:10 UTC (rev 470) +++ trunk/pybridge/pybridge/server/realm.py 2007-07-10 17:25:05 UTC (rev 471) @@ -1,5 +1,5 @@ # PyBridge -- online contract bridge made easy. -# Copyright (C) 2004-2006 PyBridge Project. +# Copyright (C) 2004-2007 PyBridge Project. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,7 +10,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -18,13 +18,14 @@ from twisted.cred import checkers, portal from twisted.spread import pb +from zope.interface import implements -from user import User, AnonymousUser +from user import AnonymousUser, RegisteredUser -class Realm: +class Realm(object): - __implements__ = portal.IRealm + implements(portal.IRealm) def requestAvatar(self, avatarId, mind, *interfaces): @@ -33,11 +34,9 @@ if avatarId == checkers.ANONYMOUS: avatar = AnonymousUser() - avatar.server = self.server # Provide reference to server. return pb.IPerspective, avatar, lambda:None else: - avatar = User(avatarId) - avatar.server = self.server # Provide reference to server. + avatar = RegisteredUser(avatarId) avatar.attached(mind) return pb.IPerspective, avatar, lambda a=avatar:a.detached(mind) Modified: trunk/pybridge/pybridge/server/server.py =================================================================== --- trunk/pybridge/pybridge/server/server.py 2007-07-10 17:19:10 UTC (rev 470) +++ trunk/pybridge/pybridge/server/server.py 2007-07-10 17:25:05 UTC (rev 471) @@ -20,7 +20,7 @@ from twisted.python import log import database as db -from pybridge import __version__ +from pybridge import __version__ as version from pybridge.network.error import DeniedRequest, IllegalRequest from pybridge.network.localtable import LocalTable @@ -30,62 +30,54 @@ from pybridge.bridge.game import BridgeGame -class Server(object): +availableTables = LocalTableManager() +onlineUsers = LocalUserManager() - def __init__(self): - # Set up rosters. - self.tables = LocalTableManager() - self.users = LocalUserManager() +def getServerInfo(): + return {'supported': (version, version), # minimum, maximum + 'version': version} - self.version = __version__ - self.supported = ['bridge'] - - def userConnects(self, user): - """""" - log.msg("User %s connected" % user.name) - self.users.userLogin(user) - db.UserAccount.byUsername(user.name).set(lastLogin=datetime.now()) - - - def userDisconnects(self, user): - """""" - log.msg("User %s disconnected" % user.name) - self.users.userLogout(user) - - # Methods invoked by user perspectives. - def registerUser(self, username, password): - """Registers a new user account in the database. - - @param username: the unique username requested by user. - @param password: the password to be associated with the account. - """ - # Check that username has not already been registered. - if db.UserAccount.selectBy(username=username).count() > 0: - raise DeniedRequest, "Username already registered" - try: - # Create user account. - db.UserAccount(username=username, password=password, allowLogin=True) - log.msg("New user %s registered" % username) - except ValueError, err: - raise IllegalRequest, err +def registerUser(username, password): + """Registers a new user account in the database. + + @param username: the unique username requested by user. + @param password: the password to be associated with the account. + """ + # Check that username has not already been registered. + if db.UserAccount.selectBy(username=username).count() > 0: + raise DeniedRequest, "Username already registered" + try: + # Create user account. + db.UserAccount(username=username, password=password, allowLogin=True) + log.msg("New user %s registered" % username) + except ValueError, err: + raise IllegalRequest, err - def userChangePassword(self, user, password): - """""" - pass +def changePasswordOfUser(username, password): + """Sets the password of user to specified password. + + @param username: the user identifier. + @param password: the new password for user. + """ + pass # TODO implement - def createTable(self, tableid, tabletype): - # Ignore specified tabletype, for now. - if tableid not in self.tables: - table = LocalTable(tableid, BridgeGame) - table.id = tableid - table.server = self - self.tables.openTable(table) - #self.tables[tableid] = table +def createTable(tableid, gametype): + """Create a new table for the specified game type. + + @param tableid: a unique identifier for the table. + @param gametype: a game identifier. + """ + # TODO: convert gametype string to corresponding class. + if tableid not in availableTables: + table = LocalTable(tableid, BridgeGame) # Ignore gametype for now. + # Provide table instance with a means of closing itself. + table.close = lambda: availableTables.closeTable(table) + availableTables.openTable(table) Modified: trunk/pybridge/pybridge/server/user.py =================================================================== --- trunk/pybridge/pybridge/server/user.py 2007-07-10 17:19:10 UTC (rev 470) +++ trunk/pybridge/pybridge/server/user.py 2007-07-10 17:25:05 UTC (rev 471) @@ -16,60 +16,58 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from datetime import datetime import re -#from twisted.internet import defer -#from twisted.python import failure +from twisted.python import log from twisted.spread import pb from pybridge.network.error import DeniedRequest, IllegalRequest +import database as db +import server -class User(pb.Avatar): +class RegisteredUser(pb.Avatar): + info = property(lambda self: {}) def __init__(self, name): self.name = name # User name. - self.server = None # Set by Realm. - self.tables = {} # For each joined table name, its instance. + self.accountRecord = db.UserAccount.byUsername(self.name) + self.joinedTables = {} # All tables which client is observing. + def attached(self, mind): """Called when connection to client is established.""" self.remote = mind - self.server.userConnects(self) + self.accountRecord.set(lastLogin=datetime.now()) + server.onlineUsers.userLogin(self) # Inform system of client's arrival. + log.msg("User %s connected" % self.name) def detached(self, mind): """Called when connection to client is lost.""" self.remote = None - self.server.userDisconnects(self) # Inform server. + server.onlineUsers.userLogout(self) + log.msg("User %s disconnected" % self.name) - def callEvent(self, eventName, **kwargs): - """Calls remote event listener with arguments.""" - if self.remote: - self.remote.callRemote(eventName, **kwargs) - - # Perspective methods, accessible by client. def perspective_getServerInfo(self): """Provides a dict of information about the server.""" - info = {} - info['supported'] = self.server.supported - info['version'] = self.server.version - return info + return server.getServerInfo() def perspective_getRoster(self, name): """Provides roster requested by client.""" if name == 'tables': - return self.server.tables + return server.availableTables elif name == 'users': - return self.server.users + return server.onlineUsers else: raise DeniedRequest, "Unknown roster name \'%s\'" % name @@ -80,37 +78,37 @@ raise IllegalRequest, "Invalid parameter for table identifier" elif not(0 < len(tableid) < 21) or re.search("[^A-Za-z0-9_ ]", tableid): raise IllegalRequest, "Invalid table identifier format" - elif tableid in self.server.tables: + elif tableid in server.availableTables: raise DeniedRequest, "Table name exists" - elif tabletype not in self.server.supported: - raise DeniedRequest, "Table type not suppported by this server" +# elif tabletype not in server.supported: +# raise DeniedRequest, "Table type not suppported by this server" - self.server.createTable(tableid, tabletype) + server.createTable(tableid, tabletype) return self.perspective_joinTable(tableid) # Force join to table. def perspective_joinTable(self, tableid): """Joins an existing table.""" if not isinstance(tableid, str): - raise IllegalRequest, "Invalid parameter for table name" - elif tableid not in self.server.tables: + raise IllegalRequest, "Invalid parameter for table identifier" + elif tableid not in server.availableTables: raise DeniedRequest, "No such table" - elif tableid in self.tables: + elif tableid in self.joinedTables: raise DeniedRequest, "Already joined table" - table = self.server.tables[tableid] - self.tables[tableid] = table + table = server.availableTables[tableid] + self.joinedTables[tableid] = table return table def perspective_leaveTable(self, tableid): """Leaves a table.""" if not isinstance(tableid, str): - raise IllegalRequest, "Invalid parameter for table name" - elif tableid not in self.tables: + raise IllegalRequest, "Invalid parameter for table identifier" + elif tableid not in self.joinedTables: raise DeniedRequest, "Not joined to table" - del self.tables[tableid] # Implicitly removes user from table. + del self.joinedTables[tableid] # Implicitly removes user from table. @@ -121,5 +119,5 @@ def perspective_register(self, username, password): """Create a user account with specified username and password.""" # TODO: consider defer.succeed, defer.fail, failure.Failure - self.server.registerUser(username, password) + server.registerUser(username, password) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |