Example #1
0
    def test_fetch_verified_emails(self):
        with self.expect_request(
                'https://mitoc-trips.mit.edu/data/verified_emails/',
            {'email': '*****@*****.**'},
                method='GET',
        ) as response:
            response.read.return_value = '{"primary": "*****@*****.**", "emails": ["*****@*****.**", "*****@*****.**"]}'
            primary, all_emails = other_verified_emails('*****@*****.**')

        self.assertEqual(primary, '*****@*****.**')
        self.assertEqual(all_emails, ['*****@*****.**', '*****@*****.**'])
Example #2
0
def add_membership():
    """ Process a CyberSource transaction & create/update membership. """
    data = request.form
    if data['decision'] != 'ACCEPT':
        return json.jsonify(), 204  # Transaction canceled, declined, etc.
    if data['req_merchant_defined_data1'] != 'membership':
        return json.jsonify(), 204  # Some other payment, we don't care

    # If we lack the secret key to verify signatures, we can rely on the web
    # server itself to provide access control (and skip signature verification)
    if current_app.config['VERIFY_CYBERSOURCE_SIGNATURE']:
        secret_key = current_app.config['CYBERSOURCE_SECRET_KEY']
        if not signature_valid(data, secret_key):
            return json.jsonify(), 401

    # From the given email, ask the trips database for all their verified emails
    email = data['req_merchant_defined_data3']  # NOT req_bill_to_email
    primary, all_emails = other_verified_emails(email)

    # Identify datetime (in UTC) when the transaction was completed
    dt_paid = datetime.strptime(data['signed_date_time'],
                                CYBERSOURCE_DT_FORMAT)

    # Fetch membership, ideally for primary email, but otherwise most recent
    person_id = db.person_to_update(primary, all_emails)
    if person_id and db.already_inserted_membership(person_id, dt_paid):
        return json.jsonify(), 202  # Most likely already processed

    # If no membership exists, create one under the primary email
    if not person_id:
        first_name = data['req_bill_to_forename']
        last_name = data['req_bill_to_surname']
        person_id = db.add_person(first_name, last_name, primary)

    two_letter_affiliation_code = data.get('req_merchant_defined_data2')
    _, expires = db.add_membership(person_id, data['req_amount'], dt_paid,
                                   two_letter_affiliation_code)
    db.commit()

    try:
        update_membership(primary, membership_expires=expires)
    except URLError:
        if extensions.sentry:
            extensions.sentry.captureException()

    return json.jsonify(), 201
Example #3
0
def add_waiver():
    """ Process a DocuSign waiver completion.

    NOTE: It's extremely important that there be some access control behind
    this route. It parses XML directly, so it must come from a trusted source
    (the xml library is vulnerable to the 'billion laughs' and quadratic blowup
    vulnerabilities). Obviously, this route also inserts rows into a database,
    so we should only be doing that based on verified information.

    DocuSign event notifications are signed with their X.509 certificate, which
    should be verified with NGINX, Apache, or similar before being forwarded to
    this route.
    """
    env = CompletedEnvelope(request.data)
    if not env.completed:
        return json.jsonify(), 204  # Still awaiting guardian's signature

    email, time_signed = env.releasor_email, env.time_signed

    primary, all_emails = other_verified_emails(email)
    person_id = db.person_to_update(primary, all_emails)
    if not person_id:
        person_id = db.add_person(env.first_name, env.last_name, primary)

    if db.already_added_waiver(person_id, time_signed):
        return json.jsonify(), 204  # Nothing more to do

    _, expires = db.add_waiver(person_id, time_signed)
    # The affiliation stated on the waiver is the most recent we know!
    db.update_affiliation(person_id, env.affiliation)
    db.commit()

    try:
        update_membership(primary, waiver_expires=expires)
    except URLError:
        if extensions.sentry:
            extensions.sentry.captureException()

    return json.jsonify(), 201