Add new Remote DB with login server! TO-DO: Name system

This commit is contained in:
DenialMC 2015-04-05 13:49:09 +03:00
parent 6436cb9f0f
commit 8a401c4687
2 changed files with 32 additions and 142 deletions

View file

@ -34,7 +34,6 @@ backups-extension .json
# Server: # Server:
server-timezone EST/EDT/-5 server-timezone EST/EDT/-5
server-port 7199 server-port 7199
account-server-endpoint https://toontownunited.com/api/
account-bridge-filename astron/databases/account-bridge.db account-bridge-filename astron/databases/account-bridge.db
# Performance: # Performance:

View file

@ -28,10 +28,8 @@ if accountDBType == 'remote':
# developer server: # developer server:
minAccessLevel = simbase.config.GetInt('min-access-level', 100) minAccessLevel = simbase.config.GetInt('min-access-level', 100)
accountServerEndpoint = simbase.config.GetString( accountServerEndpoint = simbase.config.GetString('account-server-endpoint', 'http://127.0.0.1:45749/')
'account-server-endpoint', 'https://toontownunited.com/api/') accountServerSecret = simbase.config.GetString('account-server-secret', '69')
accountServerSecret = simbase.config.GetString(
'account-server-secret', '6163636f756e7473')
http = HTTPClient() http = HTTPClient()
@ -39,23 +37,20 @@ http.setVerifySsl(0)
def executeHttpRequest(url, **extras): def executeHttpRequest(url, **extras):
timestamp = str(int(time.time()))
signature = hmac.new(accountServerSecret, timestamp, hashlib.sha256)
request = urllib2.Request(accountServerEndpoint + url) request = urllib2.Request(accountServerEndpoint + url)
request.add_header('User-Agent', 'TTU-CSM') request.add_header('User-Agent', 'TTU-Uberdog')
request.add_header('X-CSM-Timestamp', timestamp) request.add_header('Secret-Key', accountServerSecret)
request.add_header('X-CSM-Signature', signature.hexdigest())
for k, v in extras.items(): for k, v in extras.items():
request.add_header('X-CSM-' + k, v) request.add_header(k, v)
try: try:
return urllib2.urlopen(request).read() return urllib2.urlopen(request).read()
except: except:
return None return None
blacklist = executeHttpRequest('names/blacklist.json') #blacklist = executeHttpRequest('names/blacklist.json')
if blacklist: #if blacklist:
blacklist = json.loads(blacklist) # blacklist = json.loads(blacklist)
def judgeName(name): def judgeName(name):
@ -76,7 +71,7 @@ def judgeName(name):
# These classes make up the available account databases for Toontown United. # These classes make up the available account databases for Toontown United.
# Databases with login tokens use the PyCrypto module for decrypting them. # Databases with login tokens use the PyCrypto module for decrypting them.
# DeveloperAccountDB is a special database that accepts a username, and assigns # DeveloperAccountDB is a special database that accepts a username, and assigns
# each user with 600 access automatically upon login. # each user with 700 access automatically upon login.
class AccountDB: class AccountDB:
@ -119,13 +114,13 @@ class DeveloperAccountDB(AccountDB):
if str(username) not in self.dbm: if str(username) not in self.dbm:
# Nope. Let's associate them with a brand new Account object! We # Nope. Let's associate them with a brand new Account object! We
# will assign them with 600 access just because they are a # will assign them with 700 access just because they are a
# developer: # developer:
response = { response = {
'success': True, 'success': True,
'userId': username, 'userId': username,
'accountId': 0, 'accountId': 0,
'accessLevel': max(600, minAccessLevel) 'accessLevel': max(700, minAccessLevel)
} }
callback(response) callback(response)
return response return response
@ -141,68 +136,20 @@ class DeveloperAccountDB(AccountDB):
callback(response) callback(response)
return response return response
# This is the same as the DeveloperAccountDB, except it doesn't automatically
# give the user an access level of 600. Instead, the first user that is created
# gets 700 access, and every user created afterwards gets 100 access:
class LocalAccountDB(AccountDB):
notify = directNotify.newCategory('LocalAccountDB')
def lookup(self, username, callback):
# Let's check if this user's ID is in your account database bridge:
if str(username) not in self.dbm:
# Nope. Let's associate them with a brand new Account object!
response = {
'success': True,
'userId': username,
'accountId': 0,
'accessLevel': max((700 if not self.dbm else 100), minAccessLevel)
}
callback(response)
return response
else:
# We have an account already, let's return what we've got:
response = {
'success': True,
'userId': username,
'accountId': int(self.dbm[str(username)])
}
callback(response)
return response
class RemoteAccountDB(AccountDB): class RemoteAccountDB(AccountDB):
notify = directNotify.newCategory('RemoteAccountDB') notify = directNotify.newCategory('RemoteAccountDB')
def addNameRequest(self, avId, name): def addNameRequest(self, avId, name):
return executeHttpRequest('names/append', ID=str(avId), Name=name) return None#executeHttpRequest('names/append', ID=str(avId), Name=name)
def getNameStatus(self, avId): def getNameStatus(self, avId):
return executeHttpRequest('names/status/?Id=' + str(avId)) return None#executeHttpRequest('names/status/?Id=' + str(avId))
def removeNameRequest(self, avId): def removeNameRequest(self, avId):
return executeHttpRequest('names/remove', ID=str(avId)) return None#executeHttpRequest('names/remove', ID=str(avId))
def lookup(self, token, callback): def lookup(self, token, callback):
# First, base64 decode the token: if (not token) or (len(token) != 36):
try:
token = base64.b64decode(token)
except TypeError:
self.notify.warning('Could not decode the provided token!')
response = {
'success': False,
'reason': "Can't decode this token."
}
callback(response)
return response
# Ensure this token is a valid size:
if (not token) or ((len(token) % 16) != 0):
self.notify.warning('Invalid token length!') self.notify.warning('Invalid token length!')
response = { response = {
'success': False, 'success': False,
@ -211,82 +158,28 @@ class RemoteAccountDB(AccountDB):
callback(response) callback(response)
return response return response
# Next, decrypt the token using AES-128 in CBC mode: cookie = json.loads(executeHttpRequest('cookie', Cookie=token))
accountServerSecret = simbase.config.GetString(
'account-server-secret', '6163636f756e7473')
# Ensure that our secret is the correct size: if 'error' in cookie:
if len(accountServerSecret) > AES.block_size: reason = cookie['error']
self.notify.warning('account-server-secret is too big!') self.notify.warning(reason)
accountServerSecret = accountServerSecret[:AES.block_size]
elif len(accountServerSecret) < AES.block_size:
self.notify.warning('account-server-secret is too small!')
accountServerSecret += '\x80'
while len(accountServerSecret) < AES.block_size:
accountServerSecret += '\x00'
# Take the initialization vector off the front of the token:
iv = token[:AES.block_size]
# Truncate the token to get our cipher text:
cipherText = token[AES.block_size:]
# Decrypt!
cipher = AES.new(accountServerSecret, mode=AES.MODE_CBC, IV=iv)
try:
token = json.loads(cipher.decrypt(cipherText).replace('\x00', ''))
if ('timestamp' not in token) or (not isinstance(token['timestamp'], int)):
raise ValueError
if ('userid' not in token) or (not isinstance(token['userid'], int)):
raise ValueError
if ('accesslevel' not in token) or (not isinstance(token['accesslevel'], int)):
raise ValueError
except ValueError, e:
print e
self.notify.warning('Invalid token.')
response = { response = {
'success': False, 'success': False,
'reason': 'Invalid token.' 'reason': reason
} }
callback(response) callback(response)
return response return response
# Next, check if this token has expired: username = str(cookie['username'])
expiration = simbase.config.GetInt('account-token-expiration', 1800) accessLevel = max(cookie['accessLevel'], minAccessLevel)
tokenDelta = int(time.time()) - token['timestamp'] response = {
if tokenDelta > expiration: 'success': True,
response = { 'userId': username,
'success': False, 'accountId': int(self.dbm[username]) if username in self.dbm else 0,
'reason': 'This token has expired.' 'accessLevel': accessLevel
} }
callback(response) callback(response)
return response return response
# This token is valid. That's all we need to know. Next, let's check if
# this user's ID is in your account database bridge:
if str(token['userid']) not in self.dbm:
# Nope. Let's associate them with a brand new Account object!
response = {
'success': True,
'userId': token['userid'],
'accountId': 0,
'accessLevel': max(int(token['accesslevel']), minAccessLevel)
}
callback(response)
return response
else:
# Yep. Let's return their account ID and access level!
response = {
'success': True,
'userId': token['userid'],
'accountId': int(self.dbm[str(token['userid'])]),
'accessLevel': max(int(token['accesslevel']), minAccessLevel)
}
callback(response)
return response
# --- FSMs --- # --- FSMs ---
@ -1060,8 +953,6 @@ class ClientServicesManagerUD(DistributedObjectGlobalUD):
# Instantiate our account DB interface: # Instantiate our account DB interface:
if accountDBType == 'developer': if accountDBType == 'developer':
self.accountDB = DeveloperAccountDB(self) self.accountDB = DeveloperAccountDB(self)
elif accountDBType == 'local':
self.accountDB = LocalAccountDB(self)
elif accountDBType == 'remote': elif accountDBType == 'remote':
self.accountDB = RemoteAccountDB(self) self.accountDB = RemoteAccountDB(self)
else: else: