def _get_user_via_basic_auth(auth_header): """Given a basic auth header, return a User object. """ try: creds = binascii.a2b_base64(auth_header[len('Basic '):]).split(':', 1) except binascii.Error: raise Response(400, 'Malformed "Authorization" header') if len(creds) != 2: raise Response(401) userid, api_key = creds if len(userid) == 36 and '-' in userid: user = _get_user_via_api_key(userid) # For backward-compatibility else: try: userid = int(userid) except ValueError: raise Response(401) user = User.from_id(userid) if user.ANON or not constant_time_compare(user.participant.api_key, api_key): raise Response(401) return user
def check_connect_token(self, token): return ( self.connect_token and constant_time_compare(self.connect_token, token) and self.connect_expires > utcnow() )
def finish_email_verification(self, email, nonce): """Given an email address and a nonce as strings, return a three-tuple: - a ``VERIFICATION_*`` constant; - a list of packages if ``VERIFICATION_SUCCEEDED`` (``None`` otherwise), and - a boolean indicating whether the participant's PayPal address was updated if applicable (``None`` if not). """ _fail = VERIFICATION_FAILED, None, None if '' in (email.strip(), nonce.strip()): return _fail with self.db.get_cursor() as cursor: # Load an email record. Check for an address match, but don't check # the nonce at this point. We want to compare in constant time to # avoid timing attacks, and we'll do that below. record = self.get_email(email, cursor, and_lock=True) if record is None: # We don't have that email address on file. Maybe it used to be # on file but was explicitly removed (they followed an old link # after removing in the UI?), or maybe it was never on file in # the first place (they munged the querystring?). return _fail if record.nonce is None: # Nonces are nulled out only when updating to mark an email # address as verified; we always set a nonce when inserting. # Therefore, the main way to get a null nonce is to issue a # link, follow it, and follow it again. # All records with a null nonce should be verified, though not # all verified records will have a null nonce. That is, it's # possible to land here with an already-verified address, and # this is in fact expected when verifying package ownership via # an already-verified address. assert record.verified return VERIFICATION_REDUNDANT, None, None # *Now* verify that the nonce given matches the one expected, along # with the time window for verification. if not constant_time_compare(record.nonce, nonce): return _fail if (utcnow() - record.verification_start) > EMAIL_HASH_TIMEOUT: return _fail # And now we can load any packages associated with the nonce, and # save the address. packages = self.get_packages_claiming(cursor, nonce) paypal_updated = None try: if packages: paypal_updated = False self.finish_package_claims(cursor, nonce, *packages) self.save_email_address(cursor, email) has_no_paypal = not self.get_payout_routes(good_only=True) if packages and has_no_paypal: self.set_paypal_address(email, cursor) paypal_updated = True except IntegrityError: return VERIFICATION_STYMIED, None, None return VERIFICATION_SUCCEEDED, packages, paypal_updated