def emailEffect(effect, info): """Email Effect Sends the effect to a developer so they are aware of the error Arguments: effect {Services.Effect} -- The effect to send info {dict} -- Info about the request Returns: None """ # Try to send an e-mail oEffect = Services.create('communications', 'email', { "_internal_": Services.internalKey(), "from": "noreply@%s" % Conf.get(("domain", "primary")), "subject": "external error", "text_body": "Info: %s\nEffect: %s" % (str(info), str(effect)), "to": Conf.get(("email", "errors")) }) # If there's an error if oEffect.errorExists(): raise oEffect
def matchRequest_update(self, data, sesh): """Match Request (Update) Accepts a match request and creates the match with the proper service, then notifies both throwers of the ID of the new match Arguments: data {dict} -- Data sent with the request sesh {Sesh._Session} -- The session associated with the request Returns: Services.Effect """ # Verify fields try: DictHelper.eval(data, ['id']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Find the request oRequest = MatchRequest.get(data['id']) if not oRequest: return Services.Effect(error=(1104, 'match_request:%s' % data['id'])) # If the accepter is not the opponent if sesh['thrower']['_id'] != oRequest['opponent']: return Services.Effect(error=1000) # Create a new match in the proper service oEffect = Services.create( oRequest['org'].lower(), 'match', { "_internal_": Services.internalKey(), "initiator": oRequest['initiator'], "opponent": oRequest['opponent'] }, sesh) if oEffect.errorExists(): return oEffect # Delete the request oRequest.delete() # Notify the initiator of the new match Sync.push('auth', 'request-%s' % data['id'], { "type": "accepted", "match": oEffect.data }) # Return the ID of the new match return Services.Effect(oEffect.data)
def match_create(self, data, sesh): """Match (Create) Creates a new match and returns its ID Arguments: data {dict} -- Data sent with the request sesh {Sesh._Session} -- Session associated with the request Returns: Services.Effect """ # Verify fields try: DictHelper.eval(data, ['_internal_', 'initiator', 'opponent']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Verify the key, remove it if it's ok if not Services.internalKey(data['_internal_']): return Services.Effect(error=Errors.SERVICE_INTERNAL_KEY) del data['_internal_'] # Create a new match instance try: oMatch = Match({ "_created": int(time()), "finished": False, "calculated": False, "initiator": data['initiator'], "opponent": data['opponent'], "game_finished": { "i": False, "o": False }, "game": { "i": {}, "o": {} } }) except ValueError as e: return Services.Effect(error=(1001, e.args[0])) # Store the instance if not oMatch.create(): return Services.Effect(error=1100) # Return the ID return Services.Effect(oMatch['_id'])
def matchUnfinished_read(self, data, sesh): """Match Unfinished Returns any unfinished matches the thrower is involved in Arguments: data {dict} -- Data sent with the request sesh {Sesh._Session} -- Session associated with the request Returns: Services.Effect """ # Find all unfinished matches the thrower is involved in lMatches = Match.unfinished(sesh['thrower']['_id']) # If there's none if not lMatches: return Services.Effect([]) # Get the other throwers lThrowers = [] for d in lMatches: lThrowers.append( d['initiator'] == sesh['thrower']['_id'] and d['opponent'] or d['initiator']) # If there's any throwers dAliases = {} if lThrowers: oEffect = Services.read('auth', 'thrower/aliases', { "_internal_": Services.internalKey(), "ids": list(set(lThrowers)) }) if oEffect.errorExists(): return oEffect dAliases = oEffect.data # Add the aliases to each record for d in lMatches: s = d['initiator'] == sesh['thrower']['_id'] and d[ 'opponent'] or d['initiator'] d['alias'] = s in dAliases and dAliases[s] or 'N/A' # Return the matches return Services.Effect(lMatches)
def throwerVerify_update(self, data): """Thrower Verify Sets the thrower's email to verified if the key is valid Arguments: data {dict} -- Data sent with the request Returns: Services.Effect """ # Verify fields try: DictHelper.eval(data, ['_internal_', 'id', 'verify']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Verify the key, remove it if it's ok if not Services.internalKey(data['_internal_']): return Services.Effect(error=Errors.SERVICE_INTERNAL_KEY) del data['_internal_'] # Find the thrower oThrower = Thrower.get(data['id']) # If it doesn't exist if not oThrower: return Services.Effect(error=(1104, data['id'])) # If the thrower is already verified if oThrower['verified'] == True: return Services.Effect(True) # If the code is not valid if data['verify'] != oThrower['verified']: return Services.Effect(error=1205) # Update the thrower oThrower['verified'] = True oThrower.save(changes=False) # Return OK return Services.Effect(True)
def verify(id, key): # Contact the auth service to verify the key oEffect = Services.update('auth', 'thrower/verify', { "_internal_": Services.internalKey(), "id": id, "verify": key }) # If there's an error if oEffect.errorExists(): print(oEffect) emailEffect(oEffect, {"request":"verify", "id": id, "key":key}) return show500() # Redirect to main site dConf = Conf.get("domain") bottle.redirect("%s://%s/#verified" % ( dConf['protocol'], dConf['primary'] ))
def match_read(self, data, sesh): """Match (Read) Fetches and returns the stats from an existing match Arguments: data {dict} -- Data sent with the request sesh {Sesh._Session} -- Session associated with the request Returns: Services.Effect """ # Verify fields try: DictHelper.eval(data, ['id']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Find the match dMatch = Match.get(data['id'], raw=True) if not dMatch: return Services.Effect(error=(1104, 'watl_match:%s' % data['id'])) # Get the aliases of both throwers oEffect = Services.read( 'auth', 'thrower/aliases', { "_internal_": Services.internalKey(), "ids": [dMatch['opponent'], dMatch['initiator']] }) if oEffect.errorExists(): return oEffect # Add the aliases dMatch['initiator_alias'] = oEffect.data[dMatch['initiator']] dMatch['opponent_alias'] = oEffect.data[dMatch['opponent']] # Else return the match return Services.Effect(dMatch)
def throwerAliases_read(self, data): """Thrower Aliases Recieves a list of thrower IDs and returns a dictionary of IDs to aliases Arguments: data {dict} -- Data sent with the request Returns: Effect """ # Verify fields try: DictHelper.eval(data, ['_internal_', 'ids']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Verify the key, remove it if it's ok if not Services.internalKey(data['_internal_']): return Services.Effect(error=Errors.SERVICE_INTERNAL_KEY) del data['_internal_'] # If the IDs are not a list if not isinstance(data['ids'], list): return Services.Effect(error=(1001, [('ids', 'not a list')])) # If the list is empty if not data['ids']: return Services.Effect({}) # Get and return all the thrower aliases return Services.Effect({ d['_id']: d['alias'] for d in Thrower.get(data['ids'], raw=['_id', 'alias']) })
def email(self, data): """Email Sends an e-mail, by either sending it to the queue, or sending it directly Arguments: data {dict} -- The data sent with the request Returns: Services.Effect """ # Verify fields try: DictHelper.eval(data, ['_internal_', 'subject', 'to']) except ValueError as e: return Services.Effect(error=(1001, [(f, 'missing') for f in e.args])) # Verify the key, remove it if it's ok if not Services.internalKey(data['_internal_']): return Services.Effect(error=Errors.SERVICE_INTERNAL_KEY) del data['_internal_'] # Check that we have at least one type of body if 'html_body' not in data and 'text_body' not in data: return Services.Effect(error=1300) # Add None if either body is missing if 'html_body' not in data: data['html_body'] = None if 'text_body' not in data: data['text_body'] = None # If the from is not set if 'from' not in data: data['from'] = self.fromDefault # If we got a _queue_ value if '_queue_' in data: # Store it sQueueKey = data.pop('_queue_') # If it's not valid if not _queueKey(data, sQueueKey): return Services.Effect(error=1001) # Else, we're good data['_queue_'] = True # If we are sending direct, or we got a valid request from the queue if self.emailMethod == 'direct' or '_queue_' in data: # Init the attachments var mAttachments = None # If there's an attachment if 'attachments' in data: # Make sure it's a list if not isinstance(data['attachments'], (list, tuple)): data['attachments'] = [data['attachments']] # Loop through the attachments for i in range(len(data['attachments'])): # If we didn't get a dictionary if not isinstance(data['attachments'][i], dict): return Services.Effect(error=(1301, "attachments.%d" % i)) # If the fields are missing try: DictHelper.eval(data['attachments'][i], ['body', 'filename']) except ValueError as e: return Services.Effects(error=(1001, [("attachments.%d.%s" % (i, s), 'invalid') for s in e.args])) # Try to decode the base64 try: data['attachments'][i]['body'] = b64decode( data['attachments'][i]['body']) except TypeError: return Services.Effect(error=1302) # Set the attachments from the data mAttachments = data['attachments'] # Send the e-mail iRes = SMTP.send(data['to'], data['subject'], text_body=data['text_body'], html_body=data['html_body'], from_=data['from'], attachments=mAttachments) # If there was an error if iRes != SMTP.OK: return Services.Effect(error=(1303, '%i %s' % (iRes, SMTP.lastError()))) # Else, we are sending to the queue first else: # Add a queue key to the data data['_queue_'] = self._queueKey(data) # Send the data to the queue service oEff = Services.create( 'queue', 'msg', { "_internal_": Services.internalKey(), "service": "communications", "path": "email", "method": "create", "data": data }) # Return if there's an error if oEff.errorExists(): return oEff # Return OK return Services.Effect(True)
def throwerEmail_update(self, data, sesh): """Thrower Email Changes the email for the current signed in user Arguments: data {dict} -- Data sent with the request sesh {Sesh._Session} -- The session associated with the user Returns: Effect """ # Verify fields try: DictHelper.eval(data, ['email', 'email_passwd']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Find the thrower oThrower = Thrower.get(sesh['thrower']['_id']) if not oThrower: return Services.Effect(error=1104) # Validate the password if not oThrower.passwordValidate(data['email_passwd']): return Services.Effect(error=(1001, [('email_passwd', 'invalid')])) # Make sure the email is valid structurally if not _emailRegex.match(data['email']): return Services.Effect(error=(1001, [('email', 'invalid')])) # Look for someone else with that email dThrower = Thrower.get(data['email'], index='email', raw=['_id']) if dThrower: return Services.Effect(error=(1206, data['email'])) # Update the email and verified fields try: oThrower['email'] = data['email'] oThrower['verified'] = StrHelper.random(32, '_0x') except ValueError as e: return Services.Effect(error=(1001, e.args[0])) # Update the thrower oThrower.save(changes={"creator": sesh['thrower']['_id']}) # Send en e-mail for verification dConf = Conf.get("domain") sURL = "%s://external.%s/verify/%s/%s" % ( dConf['protocol'], dConf['primary'], oThrower['_id'], oThrower['verified']) oEffect = Services.create( 'communications', 'email', { "_internal_": Services.internalKey(), "html_body": Templates.generate('email/verify.html', {"url": sURL}, oThrower['locale']), "subject": Templates.generate('email/verify_subject.txt', {}, oThrower['locale']), "to": data['email'], }) if oEffect.errorExists(): return oEffect # Return OK return Services.Effect(True)
def signup_create(self, data): """Signup Creates a new user on the system Arguments: data {dict} -- The data passed to the request Returns: Result """ # Verify fields try: DictHelper.eval(data, ['alias', 'passwd']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Make sure the email is valid structurally if 'email' in data and not _emailRegex.match(data['email']): return Services.Effect(error=(1001, [('email', 'invalid')])) # Make sure the password is strong enough if not Thrower.passwordStrength(data['passwd']): return Services.Effect(error=1204) # Look for someone else with that alias dThrower = Thrower.get(data['alias'], index='alias', raw=['_id']) if dThrower: return Services.Effect(error=(1200, data['alias'])) # If an e-mail was passed if 'email' in data: # Look for someone else with that email dThrower = Thrower.get(data['email'], index='email', raw=['_id']) if dThrower: return Services.Effect(error=(1206, data['email'])) # If no language was passed if 'locale' not in data: data['locale'] = 'en-US' # Init the thrower data dThrower = { "_created": int(time()), "alias": data['alias'], "locale": data['locale'], "org": 'org' in data and data['org'] or 'natf', "passwd": Thrower.passwordHash(data['passwd']), "verified": StrHelper.random(32, '_0x') } # If there's an email if 'email' in data: dThrower['email'] = data['email'] dThrower['verified'] = StrHelper.random(32, '_0x') else: dThrower['verified'] = False # Create an instance try: oThrower = Thrower(dThrower) except ValueError as e: return Services.Effect(error=(1001, e.args[0])) # Store the instance if not oThrower.create(changes={"creator": "signup"}): return Services.Effect(error=1100) Services.create( 'communications', 'email', { "_internal_": Services.internalKey(), "text_body": "Alias: %s\nEmail: %s" % (data['alias'], ('email' in data and data['email'] or '')), "subject": "Axegains Signup", "to": "*****@*****.**", }) # If there's an e-mail if 'email' in data: # Send en e-mail for verification dConf = Conf.get("domain") sURL = "%s://external.%s/verify/%s/%s" % ( dConf['protocol'], dConf['primary'], oThrower['_id'], oThrower['verified']) oEffect = Services.create( 'communications', 'email', { "_internal_": Services.internalKey(), "html_body": Templates.generate('email/verify.html', {"url": sURL}, data['locale']), "subject": Templates.generate('email/verify_subject.txt', {}, data['locale']), "to": data['email'], }) if oEffect.errorExists(): return oEffect # If we don't already have a session, create one if 'session' not in data: oSesh = Sesh.create() # Else, load the existing session else: oSesh = Sesh.start(data['session']) # Add the thrower ID to it oSesh['thrower'] = oThrower.record() oSesh.save() # Return the session token return Services.Effect({ "session": oSesh.id(), "thrower": { "_id": oSesh['thrower']['_id'], "alias": oSesh['thrower']['alias'], "org": oSesh['thrower']['org'] } })
def passwdForgot_create(self, data): """Password Forgot (Generate) Creates the key that will be used to allow a user to change their password if they forgot it Arguments: data {dict} -- Data sent with the request Returns: Services.Effect """ # Verify fields try: DictHelper.eval(data, ['email']) except ValueError as e: return Services.Effect(error=(1001, [(f, "missing") for f in e.args])) # Look for the thrower by email oThrower = Thrower.get(data['email'], index='email', limit=1) if not oThrower: return Services.Effect(True) # Is there already a key in the thrower? if 'forgot' in oThrower and 'regenerate' not in data: # Is it not expired? if oThrower['forgot']['expires'] > int(time()): return Services.Effect(True) # Update the thrower with a timestamp (for expiry) and the key sKey = StrHelper.random(32, '_0x') oThrower['forgot'] = {"expires": int(time()) + 300, "key": sKey} if not oThrower.save(changes=False): return Services.Effect(error=1103) # Get the domain config dConf = Conf.get("domain") # Forgot email template variables dTpl = { "key": sKey, "url": "%s://%s/#forgot=%s" % (dConf['protocol'], dConf['primary'], sKey) } # Email the user the key oEffect = Services.create( 'communications', 'email', { "_internal_": Services.internalKey(), "html_body": Templates.generate('email/forgot.html', dTpl, oThrower['locale']), "subject": Templates.generate('email/forgot_subject.txt', {}, oThrower['locale']), "to": data['email'], }) if oEffect.errorExists(): return oEffect # Return OK return Services.Effect(True)