<commit_before># -*- coding: utf-8 -*-

#
# Copyright <NAME> 2020
# MIT License : https://adampritchard.mit-license.org/
#

"""
This module contains request handlers related to our public-facing
self-registration page and functionality. A big part of this is integration
with Paypal payment processing.
"""

from __future__ import annotations
import logging
import datetime
from urllib.parse import urlparse
import pprint
import requests
import flask
from google.cloud import ndb

import config
import helpers
import gapps
import emailer
import main


'''
This is the flow, mostly as it regards Paypal subscriptions:

1. Our member form gets loaded in an iframe on the DECA main site.
   - Same form for create and renew. We'll use email match to decide which to do.

2. User fills form, indicating payment preference, and clicks submit.
   - For now our flow only includes the Paypal payment preference.

3. Form JS sends AJAX POST to our server with user data.

4. Server generates "invoice" token for request. Stores token and user data in
   NDB. Creates Paypal URL that includes the "invoice" values. Responds 200
   with Paypal URL in body.

5. Form JS sets `window.top.location` to Paypal URL. Page redirects there.

6a. User completes the Paypal process successfully.
    - At this point the user probably goes back to the DECA main site for thanks.

7. Our server gets a Paypal IPN request indicating the payment is complete.
   This request includes our "invoice" token.
   - Renewal variation: when a subsequent automatic Paypal payment is made, we
     will receive a IPN that it occured, but there will be no "invoice" value.
     (I think. Haven't seen it happen yet.) We will use the payer ID to
     renew the correct member.

8. Our server does the counter-request to Paypal to ensure authenticity of the
   IPN request.

9a. IPN counter-request is successful.

10. We will send a taskqueue request to ourselves with the token and some of
    the Paypal payer info (the payer ID, in particular).

11. Server taskqueue handler uses the invoice token to retrieve (and remove)
    the user data in NDB.
    - If the member info is not found in NDB, attempt to use Paypal email to
      find correct member.

12. Server creates (or renews) the user. (And starts the welcome email process.)

13. The end.

--

6b. User does *not* complete the Paypal process successfully.

7. Server expires the user data in NDB after some amount of time.

8. The end.

--

9b. IPN counter-request is *not* successful.

10. Our server does not proceed.

11. If there is actually any real user data associated with the IPN request
    (which there probably isn't, because someone is messing with us), it will
    expire in NDB after some amount of time.

12. The end.

'''


self_serve = flask.Blueprint('self_serve', __name__)

self_serve.before_request(main.additional_csrf_checks)

# This will be set by the JavaScript in common.js
_EMBEDDER_VALUE_KEY = '_embedder'

_PAYMENT_METHOD_VALUE_KEY = 'payment_method'


class MemberCandidate(ndb.Model):
    """Used to store member info that hasn't yet been confirmed for full
    creation (i.e., they haven't paid yet).
    We will expire and delete these records if they don't get processed.
    """
    member_json = ndb.StringProperty()
    created = ndb.DateTimeProperty(auto_now_add=True)
    expire = ndb.DateTimeProperty()

    _ndb_client = ndb.Client()

    @classmethod
    def pop(cls, keystring: str) -> MemberCandidate:
        """Fetch and remove from the datastore the member candidate at `keystring.`
        """
        with cls._ndb_client.context():
            member_candidate_key = ndb.Key(urlsafe=keystring)
            member_candidate = member_candidate_key.get()
            member_candidate_key.delete()<commit_msg>Disable self-serve Flask-WTF CSRF check<commit_after># -*- coding: utf-8 -*-

#
# Copyright <NAME> 2020
# MIT License : https://adampritchard.mit-license.org/
#

"""
This module contains request handlers related to our public-facing
self-registration page and functionality. A big part of this is integration
with Paypal payment processing.
"""

from __future__ import annotations
import logging
import datetime
from urllib.parse import urlparse
import pprint
import requests
import flask
from google.cloud import ndb

import config
import helpers
import gapps
import emailer
import main


'''
This is the flow, mostly as it regards Paypal subscriptions:

1. Our member form gets loaded in an iframe on the DECA main site.
   - Same form for create and renew. We'll use email match to decide which to do.

2. User fills form, indicating payment preference, and clicks submit.
   - For now our flow only includes the Paypal payment preference.

3. Form JS sends AJAX POST to our server with user data.

4. Server generates "invoice" token for request. Stores token and user data in
   NDB. Creates Paypal URL that includes the "invoice" values. Responds 200
   with Paypal URL in body.

5. Form JS sets `window.top.location` to Paypal URL. Page redirects there.

6a. User completes the Paypal process successfully.
    - At this point the user probably goes back to the DECA main site for thanks.

7. Our server gets a Paypal IPN request indicating the payment is complete.
   This request includes our "invoice" token.
   - Renewal variation: when a subsequent automatic Paypal payment is made, we
     will receive a IPN that it occured, but there will be no "invoice" value.
     (I think. Haven't seen it happen yet.) We will use the payer ID to
     renew the correct member.

8. Our server does the counter-request to Paypal to ensure authenticity of the
   IPN request.

9a. IPN counter-request is successful.

10. We will send a taskqueue request to ourselves with the token and some of
    the Paypal payer info (the payer ID, in particular).

11. Server taskqueue handler uses the invoice token to retrieve (and remove)
    the user data in NDB.
    - If the member info is not found in NDB, attempt to use Paypal email to
      find correct member.

12. Server creates (or renews) the user. (And starts the welcome email process.)

13. The end.

--

6b. User does *not* complete the Paypal process successfully.

7. Server expires the user data in NDB after some amount of time.

8. The end.

--

9b. IPN counter-request is *not* successful.

10. Our server does not proceed.

11. If there is actually any real user data associated with the IPN request
    (which there probably isn't, because someone is messing with us), it will
    expire in NDB after some amount of time.

12. The end.

'''


self_serve = flask.Blueprint('self_serve', __name__)

# TODO: Flask-WTF CSRF check is sometimes failing, even though the form field and the
# header are obviously present. I'm going to disable that check and leave the additional
# checks in place.
main.csrf.exempt(self_serve)

self_serve.before_request(main.additional_csrf_checks)

# This will be set by the JavaScript in common.js
_EMBEDDER_VALUE_KEY = '_embedder'

_PAYMENT_METHOD_VALUE_KEY = 'payment_method'


class MemberCandidate(ndb.Model):
    """Used to store member info that hasn't yet been confirmed for full
    creation (i.e., they haven't paid yet).
    We will expire and delete these records if they don't get processed.
    """
    member_json = ndb.StringProperty()
    created = ndb.DateTimeProperty(auto_now_add=True)
    expire = ndb.DateTimeProperty()

    _ndb_client = ndb.Client()

    @classmethod
    def pop(cls, keystring: str) -> MemberCandidate:
        """Fetch and remove from the datastore the member candidate at `keystring.`
        """
        with cls._ndb_client.context():
            member_candidate_key = ndb.Key(urlsafe=keystring)
            member_candidate = member_candidate_key.get()
            member_candidate_key.delete()
{'fraction_non_alphanumeric': 0.06408288564850345, 'fraction_numerical': 0.008953696597595293, 'mean_word_length': 3.473112128146453, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6666433', 'n_tokens_mistral': 2284, 'n_tokens_neox': 2186, 'n_words': 1055}