csmud: adjust remotedb

This commit is contained in:
Master Jumblespeed 2015-07-04 15:13:29 -04:00
parent 0c04d9d876
commit 49b26c4103
3 changed files with 82 additions and 33 deletions

View file

@ -87,7 +87,7 @@ def checkName(name, otherCheckFuncs = [], font = None):
tn = TextNode('NameCheck') tn = TextNode('NameCheck')
tn.setFont(font) tn.setFont(font)
for c in name: for c in name:
if not tn.hasCharacter(unicode(c)): if not tn.hasCharacter(c.decode('utf-8')):
notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(c)) notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(c))
return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(c) return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(c)

View file

@ -1,29 +1,30 @@
#!/usr/bin/env python2
import json import json
import os import os
import requests import requests
from panda3d.core import * from pandac.PandaModules import *
username = os.environ['ttsUsername'] username = os.environ['ttsUsername']
password = os.environ['ttsPassword'] password = os.environ['ttsPassword']
accountServerEndpoint = ConfigVariableString( accountServerEndpoint = 'http://www.toontownstride.com/api/'
'account-server-endpoint', session = requests.Session()
'http://tigercat1.me/tmpremote/api/').getValue() csrf_query = session.get(accountServerEndpoint + 'login/')
request = requests.post( csrf = session.cookies.get_dict().get('csrftoken', '')
request = session.post(
accountServerEndpoint + 'login/', accountServerEndpoint + 'login/',
data={'n': username, 'p': password}) data={'username': username, 'password': password, 'csrfmiddlewaretoken': csrf})
try: try:
response = json.loads(request.text) response = json.loads('{'+request.text.split('{', 1)[1]) # so that we ignore the csrf token
except ValueError: except ValueError:
print "Couldn't verify account credentials." print "Couldn't verify account credentials."
else: else:
if not response['success']: if response['status'] != 7:
print response['reason'] print response['message']
else: else:
os.environ['TTS_PLAYCOOKIE'] = response['token'] os.environ['TTS_PLAYCOOKIE'] = response['token']
os.environ['TTS_GAMESERVER'] = response['gameserver']
# Start the game: # Start the game:
import toontown.toonbase.ClientStart import toontown.toonbase.ClientStart

View file

@ -15,7 +15,7 @@ from panda3d.core import *
import hashlib, hmac, json import hashlib, hmac, json
import anydbm, math, os import anydbm, math, os
import urllib2, time import urllib2, time, urllib, base64
def rejectConfig(issue, securityIssue=True, retarded=True): def rejectConfig(issue, securityIssue=True, retarded=True):
print print
@ -42,10 +42,14 @@ def entropyIdeal(length):
accountDBType = config.GetString('accountdb-type', 'developer') accountDBType = config.GetString('accountdb-type', 'developer')
accountServerSecret = config.GetString('account-server-secret', 'dev') accountServerSecret = config.GetString('account-server-secret', 'dev')
accountServerHashAlgo = config.GetString('account-server-hash-algo', 'sha512') accountServerHashAlgo = config.GetString('account-server-hash-algo', 'sha512')
apiSecret = accountServerSecret = config.GetString('api-key', 'dev')
if accountDBType == 'remote': if accountDBType == 'remote':
if accountServerSecret == 'dev': if accountServerSecret == 'dev':
rejectConfig('you have not changed the secret in config/local.prc') rejectConfig('you have not changed the secret in config/local.prc')
if apiSecret == 'dev':
rejectConfig('you have not changed the api key in config/local.prc')
if len(accountServerSecret) < 16: if len(accountServerSecret) < 16:
rejectConfig('the secret is too small! Make it 16+ bytes', retarded=False) rejectConfig('the secret is too small! Make it 16+ bytes', retarded=False)
@ -67,18 +71,17 @@ minAccessLevel = config.GetInt('min-access-level', 100)
def executeHttpRequest(url, **extras): def executeHttpRequest(url, **extras):
# TO DO: THIS IS QUITE DISGUSTING # TO DO: THIS IS QUITE DISGUSTING
# INSTEAD OF USING THE SAME SECRET, WE SHOULD HAVE AN API KEY EXCLUSIVE TO THAT
# MOVE THIS TO ToontownInternalRepository (this might be interesting for AI) # MOVE THIS TO ToontownInternalRepository (this might be interesting for AI)
request = urllib2.Request('http://tigercat1.me/tmpremote/api/' + url) _data = {}
timestamp = str(int(time.time())) if len(extras.items()) != 0:
signature = hashlib.sha256(timestamp + accountServerSecret + "h*^ahJGHA017JI&A&*uyhU07")
request.add_header('User-Agent', 'TTS-CSM')
request.add_header('X-CSM-Timestamp', timestamp)
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) _data[k] = v
signature = hashlib.sha512(json.dumps(_data) + apiSecret).hexdigest()
data = urllib.urlencode({'data': json.dumps(_data), 'hmac': signature})
req = urllib2.Request('http://www.toontownstride.com/api/' + url, data)
req.get_method = lambda: "POST"
try: try:
return urllib2.urlopen(request).read() return urllib2.urlopen(req).read()
except: except:
return None return None
@ -103,7 +106,8 @@ def executeHttpRequestAndLog(url, **extras):
return data return data
blacklist = executeHttpRequest('names/blacklist.json') #blacklist = executeHttpRequest('names/blacklist.json') # todo; create a better system for this
blacklist = json.dumps(["none"])
if blacklist: if blacklist:
blacklist = json.loads(blacklist) blacklist = json.loads(blacklist)
@ -139,7 +143,7 @@ class AccountDB:
def addNameRequest(self, avId, name): def addNameRequest(self, avId, name):
return True return True
def getNameStatus(self, avId): def getNameStatus(self, avId, callback = None):
return 'APPROVED' return 'APPROVED'
def removeNameRequest(self, avId): def removeNameRequest(self, avId):
@ -183,17 +187,36 @@ class RemoteAccountDB(AccountDB):
# CURRENTLY IT MAKES n REQUESTS FOR EACH AVATAR # CURRENTLY IT MAKES n REQUESTS FOR EACH AVATAR
# IN THE FUTURE, MAKE ONLY 1 REQUEST # IN THE FUTURE, MAKE ONLY 1 REQUEST
# WHICH RETURNS ALL PENDING AVS # WHICH RETURNS ALL PENDING AVS
# ^ done, check before removing todo note
notify = directNotify.newCategory('RemoteAccountDB') notify = directNotify.newCategory('RemoteAccountDB')
def addNameRequest(self, avId, name):
return executeHttpRequest('names/append', ID=str(avId), Name=name)
def getNameStatus(self, avId): def addNameRequest(self, avId, name, accountID = None):
#return executeHttpRequest('names/status/?Id=' + str(avId)) username = avId
return 'APPROVED' # Override temporarily. if accountID is not None:
username = accountID
res = executeHttpRequest('names', action='set', username=str(username),
avId=str(avId), wantedName=name)
if res is not None:
return True
return False
def getNameStatus(self, accountId, callback = None):
r = executeHttpRequest('names', action='get', username=str(accountId))
try:
r = json.loads(r)
if callback is not None:
callback(r)
return True
except:
return False
def removeNameRequest(self, avId): def removeNameRequest(self, avId):
return executeHttpRequest('names/remove', ID=str(avId)) r = executeHttpRequest('names', action='del', avId=str(avId))
if r:
return 'SUCCESS'
return 'FAILURE'
def lookup(self, token, callback): def lookup(self, token, callback):
''' '''
@ -213,7 +236,6 @@ class RemoteAccountDB(AccountDB):
try: try:
token = token.decode('base64') token = token.decode('base64')
hash, token = token[:hashSize], token[hashSize:] hash, token = token[:hashSize], token[hashSize:]
correctHash = hashAlgo(token + accountServerSecret).digest() correctHash = hashAlgo(token + accountServerSecret).digest()
if len(hash) != len(correctHash): if len(hash) != len(correctHash):
raise ValueError('Invalid hash.') raise ValueError('Invalid hash.')
@ -234,6 +256,7 @@ class RemoteAccountDB(AccountDB):
return AccountDB.lookup(self, token, callback) return AccountDB.lookup(self, token, callback)
# --- FSMs --- # --- FSMs ---
class OperationFSM(FSM): class OperationFSM(FSM):
TARGET_CONNECTION = False TARGET_CONNECTION = False
@ -457,6 +480,9 @@ class CreateAvatarFSM(OperationFSM):
self.account = fields self.account = fields
# For use in calling name requests:
self.accountID = self.account['ACCOUNT_ID']
self.avList = self.account['ACCOUNT_AV_SET'] self.avList = self.account['ACCOUNT_AV_SET']
# Sanitize: # Sanitize:
self.avList = self.avList[:6] self.avList = self.avList[:6]
@ -508,6 +534,8 @@ class CreateAvatarFSM(OperationFSM):
{'ACCOUNT_AV_SET': self.account['ACCOUNT_AV_SET']}, {'ACCOUNT_AV_SET': self.account['ACCOUNT_AV_SET']},
self.__handleStoreAvatar) self.__handleStoreAvatar)
self.accountID = self.account['ACCOUNT_ID']
def __handleStoreAvatar(self, fields): def __handleStoreAvatar(self, fields):
if fields: if fields:
self.demand('Kill', 'Database failed to associate the new avatar to your account!') self.demand('Kill', 'Database failed to associate the new avatar to your account!')
@ -533,6 +561,9 @@ class AvatarOperationFSM(OperationFSM):
self.account = fields self.account = fields
# For use in calling name requests:
self.accountID = self.account['ACCOUNT_ID']
self.avList = self.account['ACCOUNT_AV_SET'] self.avList = self.account['ACCOUNT_AV_SET']
# Sanitize: # Sanitize:
self.avList = self.avList[:6] self.avList = self.avList[:6]
@ -546,6 +577,7 @@ class GetAvatarsFSM(AvatarOperationFSM):
def enterStart(self): def enterStart(self):
self.demand('RetrieveAccount') self.demand('RetrieveAccount')
self.nameStateData = None
def enterQueryAvatars(self): def enterQueryAvatars(self):
self.pendingAvatars = set() self.pendingAvatars = set()
@ -585,7 +617,10 @@ class GetAvatarsFSM(AvatarOperationFSM):
if wishNameState == 'OPEN': if wishNameState == 'OPEN':
nameState = 1 nameState = 1
elif wishNameState == 'PENDING': elif wishNameState == 'PENDING':
actualNameState = self.csm.accountDB.getNameStatus(avId) if self.nameStateData is None:
self.demand('QueryNameState')
return
actualNameState = self.nameStateData[str(avId)]
self.csm.air.dbInterface.updateObject( self.csm.air.dbInterface.updateObject(
self.csm.air.dbId, self.csm.air.dbId,
avId, avId,
@ -610,6 +645,16 @@ class GetAvatarsFSM(AvatarOperationFSM):
self.csm.sendUpdateToAccountId(self.target, 'setAvatars', [potentialAvs]) self.csm.sendUpdateToAccountId(self.target, 'setAvatars', [potentialAvs])
self.demand('Off') self.demand('Off')
def enterQueryNameState(self):
def gotStates(data):
self.nameStateData = data
taskMgr.doMethodLater(0, GetAvatarsFSM.demand, 'demand-QueryAvatars',
extraArgs=[self, 'QueryAvatars'])
self.csm.accountDB.getNameStatus(self.account['ACCOUNT_ID'], gotStates)
# We should've called the taskMgr action by now.
# This inherits from GetAvatarsFSM, because the delete operation ends in a # This inherits from GetAvatarsFSM, because the delete operation ends in a
# setAvatars message being sent to the client. # setAvatars message being sent to the client.
class DeleteAvatarFSM(GetAvatarsFSM): class DeleteAvatarFSM(GetAvatarsFSM):
@ -671,6 +716,7 @@ class SetNameTypedFSM(AvatarOperationFSM):
def enterStart(self, avId, name): def enterStart(self, avId, name):
self.avId = avId self.avId = avId
self.name = name self.name = name
self.set_account_id = None
if self.avId: if self.avId:
self.demand('RetrieveAccount') self.demand('RetrieveAccount')
@ -680,6 +726,8 @@ class SetNameTypedFSM(AvatarOperationFSM):
self.demand('JudgeName') self.demand('JudgeName')
def enterRetrieveAvatar(self): def enterRetrieveAvatar(self):
if self.accountID:
self.set_account_id = self.accountID
if self.avId and self.avId not in self.avList: if self.avId and self.avId not in self.avList:
self.demand('Kill', 'Tried to name an avatar not in the account!') self.demand('Kill', 'Tried to name an avatar not in the account!')
return return
@ -703,7 +751,7 @@ class SetNameTypedFSM(AvatarOperationFSM):
status = judgeName(self.name) status = judgeName(self.name)
if self.avId and status: if self.avId and status:
if self.csm.accountDB.addNameRequest(self.avId, self.name): if self.csm.accountDB.addNameRequest(self.avId, self.name, accountID=self.set_account_id):
self.csm.air.dbInterface.updateObject( self.csm.air.dbInterface.updateObject(
self.csm.air.dbId, self.csm.air.dbId,
self.avId, self.avId,