def test_email_is_blocked(self, BlockedEmailMock): """Asking if blocked should only hit the DB once.""" BlockedEmailMock.objects.values_list.return_value = ['.ninja', 'stuff.web'] self.assertTrue(email_is_blocked('*****@*****.**')) self.assertTrue(email_is_blocked('*****@*****.**')) self.assertFalse(email_is_blocked('*****@*****.**')) self.assertEqual(BlockedEmailMock.objects.values_list.call_count, 1)
def test_email_is_blocked(self, BlockedEmailMock): """Asking if blocked should only hit the DB once.""" BlockedEmailMock.objects.values_list.return_value = [".ninja", "stuff.web"] self.assertTrue(email_is_blocked("*****@*****.**")) self.assertTrue(email_is_blocked("*****@*****.**")) self.assertFalse(email_is_blocked("*****@*****.**")) self.assertEqual(BlockedEmailMock.objects.values_list.call_count, 1)
def send_recovery_message(request): """ Send a recovery message to an email address. required form parameter: email If email not provided or not syntactically correct, returns 400. If email not known, returns 404. Otherwise, queues a task to send the message and returns 200. """ email = process_email(request.POST.get("email")) if not email: return invalid_email_response() if email_is_blocked(email): # don't let on there's a problem return HttpResponseJSON({"status": "ok"}) try: user_data = get_user_data(email=email) except NewsletterException as e: return newsletter_exception_response(e) if not user_data: return HttpResponseJSON( { "status": "error", "desc": "Email address not known", "code": errors.BASKET_UNKNOWN_EMAIL, }, 404, ) # Note: Bedrock looks for this 404 send_recovery_message_task.delay(email) return HttpResponseJSON({"status": "ok"})
def send_recovery_message(request): """ Send a recovery message to an email address. required form parameter: email If email not provided or not syntactically correct, returns 400. If email not known, returns 404. Otherwise, queues a task to send the message and returns 200. """ email = process_email(request.POST.get('email')) if not email: return invalid_email_response() if email_is_blocked(email): # don't let on there's a problem return HttpResponseJSON({'status': 'ok'}) try: user_data = get_user_data(email=email) except NewsletterException as e: return newsletter_exception_response(e) if not user_data: return HttpResponseJSON({ 'status': 'error', 'desc': 'Email address not known', 'code': errors.BASKET_UNKNOWN_EMAIL, }, 404) # Note: Bedrock looks for this 404 send_recovery_message_task.delay(email) return HttpResponseJSON({'status': 'ok'})
def subscribe_main(request): """Subscription view for use with client side JS""" form = SubscribeForm(request.POST) if form.is_valid(): data = form.cleaned_data if email_is_blocked(data['email']): statsd.incr('news.views.subscribe_main.email_blocked') # don't let on there's a problem return respond_ok(request, data) data['format'] = data.pop('fmt') or 'H' if data['lang']: if not language_code_is_valid(data['lang']): data['lang'] = 'en' # if lang not provided get the best one from the accept-language header else: lang = get_best_request_lang(request) if lang: data['lang'] = lang else: del data['lang'] # now ensure that if we do have a lang that it's a supported one if 'lang' in data: data['lang'] = get_best_supported_lang(data['lang']) # if source_url not provided we should store the referrer header # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data['source_url'] and request.META.get('HTTP_REFERER'): referrer = request.META['HTTP_REFERER'] if SOURCE_URL_RE.match(referrer): statsd.incr('news.views.subscribe_main.use_referrer') data['source_url'] = referrer if is_ratelimited(request, group='basket.news.views.subscribe_main', key=lambda x, y: '%s-%s' % (':'.join(data['newsletters']), data['email']), rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True): statsd.incr('subscribe.ratelimited') return respond_error(request, form, 'Rate limit reached', 429) try: upsert_user.delay(SUBSCRIBE, data, start_time=time()) except Exception: return respond_error(request, form, 'Unknown error', 500) return respond_ok(request, data) else: # form is invalid if request.is_ajax(): return HttpResponseJSON({ 'status': 'error', 'errors': format_form_errors(form.errors), 'errors_by_field': form.errors, }, 400) else: return render(request, 'news/formerror.html', {'form': form}, status=400)
def subscribe_main(request): """Subscription view for use with client side JS""" form = SubscribeForm(request.POST) if form.is_valid(): data = form.cleaned_data if email_is_blocked(data['email']): statsd.incr('news.views.subscribe_main.email_blocked') # don't let on there's a problem return respond_ok(request, data) data['format'] = data.pop('fmt') or 'H' if data['lang']: if not language_code_is_valid(data['lang']): data['lang'] = 'en' # if lang not provided get the best one from the accept-language header else: lang = get_best_request_lang(request) if lang: data['lang'] = lang else: del data['lang'] # if source_url not provided we should store the referrer header # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data['source_url'] and request.META.get('HTTP_REFERER'): referrer = request.META['HTTP_REFERER'] if SOURCE_URL_RE.match(referrer): statsd.incr('news.views.subscribe_main.use_referrer') data['source_url'] = referrer if is_ratelimited(request, group='basket.news.views.subscribe_main', key=lambda x, y: '%s-%s' % (':'.join(data['newsletters']), data['email']), rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True): statsd.incr('subscribe.ratelimited') return respond_error(request, form, 'Rate limit reached', 429) try: upsert_user.delay(SUBSCRIBE, data, start_time=time()) except Exception: return respond_error(request, form, 'Unknown error', 500) return respond_ok(request, data) else: # form is invalid if request.is_ajax(): return HttpResponseJSON({ 'status': 'error', 'errors': format_form_errors(form.errors), 'errors_by_field': form.errors, }, 400) else: return render(request, 'news/formerror.html', {'form': form}, status=400)
def subscribe(request): data = request.POST.dict() newsletters = data.get("newsletters", None) if not newsletters: # request.body causes tests to raise exceptions # while request.read() works. raw_request = request.read().decode() if "newsletters=" in raw_request: # malformed request from FxOS # Can't use QueryDict since the string is not url-encoded. # It will convert '+' to ' ' for example. data = dict( pair.split("=") for pair in raw_request.split("&") if "=" in pair) statsd.incr("news.views.subscribe.fxos-workaround") else: return HttpResponseJSON( { "status": "error", "desc": "newsletters is missing", "code": errors.BASKET_USAGE_ERROR, }, 400, ) if "email" not in data: return HttpResponseJSON( { "status": "error", "desc": "email is required", "code": errors.BASKET_USAGE_ERROR, }, 401, ) email = process_email(data["email"]) if not email: return invalid_email_response() data["email"] = email if email_is_blocked(data["email"]): statsd.incr("news.views.subscribe.email_blocked") # don't let on there's a problem return HttpResponseJSON({"status": "ok"}) optin = data.pop("optin", "N").upper() == "Y" sync = data.pop("sync", "N").upper() == "Y" authorized = False if optin or sync: if is_authorized(request, email): authorized = True if optin and not authorized: # for backward compat we just ignore the optin if # no valid API key is sent. optin = False if sync: if not authorized: return HttpResponseJSON( { "status": "error", "desc": "Using subscribe with sync=Y, you need to pass a " "valid `api-key` or FxA OAuth Authorization.", "code": errors.BASKET_AUTH_ERROR, }, 401, ) # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data.get("source_url") and request.META.get("HTTP_REFERER"): # try to get it from referrer statsd.incr("news.views.subscribe.use_referrer") data["source_url"] = request.META["HTTP_REFERER"] return update_user_task(request, SUBSCRIBE, data=data, optin=optin, sync=sync)
def subscribe_main(request): """Subscription view for use with client side JS""" form = SubscribeForm(request.POST) if form.is_valid(): data = form.cleaned_data if email_is_blocked(data["email"]): statsd.incr("news.views.subscribe_main.email_blocked") # don't let on there's a problem return respond_ok(request, data) data["format"] = data.pop("fmt") or "H" if data["lang"]: if not language_code_is_valid(data["lang"]): data["lang"] = "en" # if lang not provided get the best one from the accept-language header else: lang = get_best_request_lang(request) if lang: data["lang"] = lang else: del data["lang"] # now ensure that if we do have a lang that it's a supported one if "lang" in data: data["lang"] = get_best_supported_lang(data["lang"]) # if source_url not provided we should store the referrer header # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data["source_url"] and request.META.get("HTTP_REFERER"): referrer = request.META["HTTP_REFERER"] if SOURCE_URL_RE.match(referrer): statsd.incr("news.views.subscribe_main.use_referrer") data["source_url"] = referrer if is_ratelimited( request, group="basket.news.views.subscribe_main", key=lambda x, y: "%s-%s" % (":".join(data["newsletters"]), data["email"]), rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True, ): statsd.incr("subscribe.ratelimited") return respond_error(request, form, "Rate limit reached", 429) try: upsert_user.delay(SUBSCRIBE, data, start_time=time()) except Exception: return respond_error(request, form, "Unknown error", 500) return respond_ok(request, data) else: # form is invalid if request.is_ajax(): return HttpResponseJSON( { "status": "error", "errors": format_form_errors(form.errors), "errors_by_field": form.errors, }, 400, ) else: return render(request, "news/formerror.html", {"form": form}, status=400)
def get_involved(request): data = request.POST.dict() if "email" not in data: return HttpResponseJSON( { "status": "error", "desc": "email is required", "code": errors.BASKET_USAGE_ERROR, }, 401, ) if email_is_blocked(data["email"]): # don't let on there's a problem return HttpResponseJSON({"status": "ok"}) if "interest_id" not in data: return HttpResponseJSON( { "status": "error", "desc": "interest_id is required", "code": errors.BASKET_USAGE_ERROR, }, 401, ) try: Interest.objects.get(interest_id=data["interest_id"]) except Interest.DoesNotExist: return HttpResponseJSON( { "status": "error", "desc": "invalid interest_id", "code": errors.BASKET_USAGE_ERROR, }, 401, ) if "lang" not in data: return HttpResponseJSON( { "status": "error", "desc": "lang is required", "code": errors.BASKET_USAGE_ERROR, }, 401, ) if not language_code_is_valid(data["lang"]): return HttpResponseJSON( { "status": "error", "desc": "invalid language", "code": errors.BASKET_INVALID_LANGUAGE, }, 400, ) if "name" not in data: return HttpResponseJSON( { "status": "error", "desc": "name is required", "code": errors.BASKET_USAGE_ERROR, }, 401, ) if "country" not in data: return HttpResponseJSON( { "status": "error", "desc": "country is required", "code": errors.BASKET_USAGE_ERROR, }, 401, ) email = process_email(data.get("email")) if not email: return invalid_email_response() update_get_involved.delay( data["interest_id"], data["lang"], data["name"], email, data["country"], data.get("format", "H"), data.get("subscribe", False), data.get("message", None), data.get("source_url", None), ) return HttpResponseJSON({"status": "ok"})
def subscribe(request): data = request.POST.dict() newsletters = data.get('newsletters', None) if not newsletters: # request.body causes tests to raise exceptions # while request.read() works. raw_request = request.read() if 'newsletters=' in raw_request: # malformed request from FxOS # Can't use QueryDict since the string is not url-encoded. # It will convert '+' to ' ' for example. data = dict(pair.split('=') for pair in raw_request.split('&') if '=' in pair) statsd.incr('news.views.subscribe.fxos-workaround') else: return HttpResponseJSON({ 'status': 'error', 'desc': 'newsletters is missing', 'code': errors.BASKET_USAGE_ERROR, }, 400) if 'email' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'email is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) email = process_email(data['email']) if not email: return invalid_email_response() data['email'] = email if email_is_blocked(data['email']): statsd.incr('news.views.subscribe.email_blocked') # don't let on there's a problem return HttpResponseJSON({'status': 'ok'}) optin = data.pop('optin', 'N').upper() == 'Y' sync = data.pop('sync', 'N').upper() == 'Y' if optin and not has_valid_api_key(request): # for backward compat we just ignore the optin if # no valid API key is sent. optin = False if sync: if not has_valid_api_key(request): return HttpResponseJSON({ 'status': 'error', 'desc': 'Using subscribe with sync=Y, you need to pass a ' 'valid `api-key` GET or POST parameter or X-api-key header', 'code': errors.BASKET_AUTH_ERROR, }, 401) # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data.get('source_url') and request.META.get('HTTP_REFERER'): # try to get it from referrer statsd.incr('news.views.subscribe.use_referrer') data['source_url'] = request.META['HTTP_REFERER'] return update_user_task(request, SUBSCRIBE, data=data, optin=optin, sync=sync)
def get_involved(request): data = request.POST.dict() if 'email' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'email is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) if email_is_blocked(data['email']): # don't let on there's a problem return HttpResponseJSON({'status': 'ok'}) if 'interest_id' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'interest_id is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) try: Interest.objects.get(interest_id=data['interest_id']) except Interest.DoesNotExist: return HttpResponseJSON({ 'status': 'error', 'desc': 'invalid interest_id', 'code': errors.BASKET_USAGE_ERROR, }, 401) if 'lang' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'lang is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) if not language_code_is_valid(data['lang']): return HttpResponseJSON({ 'status': 'error', 'desc': 'invalid language', 'code': errors.BASKET_INVALID_LANGUAGE, }, 400) if 'name' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'name is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) if 'country' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'country is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) email = process_email(data.get('email')) if not email: return invalid_email_response() update_get_involved.delay( data['interest_id'], data['lang'], data['name'], email, data['country'], data.get('format', 'H'), data.get('subscribe', False), data.get('message', None), data.get('source_url', None), ) return HttpResponseJSON({'status': 'ok'})
def subscribe(request): data = request.POST.dict() newsletters = data.get('newsletters', None) if not newsletters: # request.body causes tests to raise exceptions # while request.read() works. raw_request = request.read() if 'newsletters=' in raw_request: # malformed request from FxOS # Can't use QueryDict since the string is not url-encoded. # It will convert '+' to ' ' for example. data = dict(pair.split('=') for pair in raw_request.split('&') if '=' in pair) statsd.incr('news.views.subscribe.fxos-workaround') else: return HttpResponseJSON({ 'status': 'error', 'desc': 'newsletters is missing', 'code': errors.BASKET_USAGE_ERROR, }, 400) if 'email' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'email is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) email = process_email(data['email']) if not email: return invalid_email_response() data['email'] = email if email_is_blocked(data['email']): statsd.incr('news.views.subscribe.email_blocked') # don't let on there's a problem return HttpResponseJSON({'status': 'ok'}) optin = data.pop('optin', 'N').upper() == 'Y' sync = data.pop('sync', 'N').upper() == 'Y' authorized = False if optin or sync: if is_authorized(request, email): authorized = True if optin and not authorized: # for backward compat we just ignore the optin if # no valid API key is sent. optin = False if sync: if not authorized: return HttpResponseJSON({ 'status': 'error', 'desc': 'Using subscribe with sync=Y, you need to pass a ' 'valid `api-key` or FxA OAuth Authorization.', 'code': errors.BASKET_AUTH_ERROR, }, 401) # NOTE this is not a typo; Referrer is misspelled in the HTTP spec # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36 if not data.get('source_url') and request.META.get('HTTP_REFERER'): # try to get it from referrer statsd.incr('news.views.subscribe.use_referrer') data['source_url'] = request.META['HTTP_REFERER'] return update_user_task(request, SUBSCRIBE, data=data, optin=optin, sync=sync)
def get_involved(request): data = request.POST.dict() if 'email' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'email is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) if email_is_blocked(data['email']): # don't let on there's a problem return HttpResponseJSON({'status': 'ok'}) if 'interest_id' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'interest_id is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) try: Interest.objects.get(interest_id=data['interest_id']) except Interest.DoesNotExist: return HttpResponseJSON({ 'status': 'error', 'desc': 'invalid interest_id', 'code': errors.BASKET_USAGE_ERROR, }, 401) if 'lang' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'lang is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) if not language_code_is_valid(data['lang']): return HttpResponseJSON({ 'status': 'error', 'desc': 'invalid language', 'code': errors.BASKET_INVALID_LANGUAGE, }, 400) if 'name' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'name is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) if 'country' not in data: return HttpResponseJSON({ 'status': 'error', 'desc': 'country is required', 'code': errors.BASKET_USAGE_ERROR, }, 401) email = process_email(data.get('email')) if not email: return invalid_email_response() update_get_involved.delay( data['interest_id'], data['lang'], data['name'], email, data['country'], data.get('format', 'H'), data.get('subscribe', False), data.get('message', None), data.get('source_url', None), ) return HttpResponseJSON({'status': 'ok'})