mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2025-01-09 17:53:50 +00:00
Add new Remote DB with login server! TO-DO: Name system
This commit is contained in:
parent
6436cb9f0f
commit
8a401c4687
2 changed files with 32 additions and 142 deletions
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue