Commit ee21bc1e by Vedran Karacic Committed by Vedran Karačić

Add address and products to SDN failure model.

parent 48700115
......@@ -29,6 +29,7 @@ class SDNCheckViewSetTests(TestCase):
self.PATH,
data=json.dumps({
'name': 'Tester',
'address': 'Testlandia',
'country': 'TE'
}),
content_type=JSON_CONTENT_TYPE
......
"""API endpoint for performing an SDN check on users."""
from oscar.core.loading import get_model
from requests.exceptions import HTTPError, Timeout
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
......@@ -6,6 +7,8 @@ from rest_framework.response import Response
from ecommerce.extensions.payment.utils import SDNClient
Basket = get_model('basket', 'Basket')
class SDNCheckViewSet(APIView):
"""Performs an SDN check for a given user."""
......@@ -18,10 +21,13 @@ class SDNCheckViewSet(APIView):
or failed.
"""
name = request.data['name']
address = request.data['address']
country = request.data['country']
hits = 0
site_configuration = request.site.siteconfiguration
basket = Basket.get_basket(request.user, site_configuration.site)
if site_configuration.enable_sdn_check:
sdn_check = SDNClient(
api_url=site_configuration.sdn_api_url,
......@@ -29,13 +35,13 @@ class SDNCheckViewSet(APIView):
sdn_list=site_configuration.sdn_api_list
)
try:
response = sdn_check.search(name, country)
response = sdn_check.search(name, address, country)
hits = response['total']
if hits > 0:
sdn_check.deactivate_user(
request.user,
request.site.siteconfiguration,
basket,
name,
address,
country,
response
)
......
......@@ -30,7 +30,7 @@ class Basket(AbstractBasket):
If no such basket exists, create a new one. If multiple such baskets exist,
merge them into one.
"""
editable_baskets = cls.objects.filter(site=site, owner=user, status__in=Basket.editable_statuses)
editable_baskets = cls.objects.filter(site=site, owner=user, status__in=cls.editable_statuses)
if len(editable_baskets) == 0:
basket = cls.create_basket(site, user)
else:
......
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-02-27 14:02
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('catalogue', '0023_auto_20170215_2234'),
('payment', '0015_auto_20170215_2229'),
]
operations = [
migrations.AddField(
model_name='sdncheckfailure',
name='address',
field=models.CharField(default='', max_length=60),
),
migrations.AddField(
model_name='sdncheckfailure',
name='products',
field=models.ManyToManyField(related_name='sdn_failures', to='catalogue.Product'),
),
]
......@@ -53,8 +53,10 @@ class SDNCheckFailure(TimeStampedModel):
""" Record of SDN check failure. """
full_name = models.CharField(max_length=255)
username = models.CharField(max_length=255)
address = models.CharField(max_length=60, default='')
country = models.CharField(max_length=2)
site = models.ForeignKey('sites.Site', verbose_name=_('Site'), null=True, blank=True, on_delete=models.SET_NULL)
products = models.ManyToManyField('catalogue.Product', related_name='sdn_failures')
sdn_check_response = JSONField()
def __unicode__(self):
......
......@@ -6,6 +6,7 @@ import mock
import httpretty
from django.conf import settings
from django.test import override_settings
from oscar.test import factories
from requests.exceptions import HTTPError, Timeout
from ecommerce.core.models import User
......@@ -42,6 +43,7 @@ class SDNCheckTests(TestCase):
def setUp(self):
super(SDNCheckTests, self).setUp()
self.name = 'Dr. Evil'
self.address = 'Top-secret lair'
self.country = 'EL'
self.user = self.create_user(full_name=self.name)
self.site_configuration = self.site.siteconfiguration
......@@ -64,6 +66,7 @@ class SDNCheckTests(TestCase):
'api_key': self.site_configuration.sdn_api_key,
'type': 'individual',
'name': self.name,
'address': self.address,
'countries': self.country
})
sdn_check_url = '{api_url}?{params}'.format(
......@@ -79,15 +82,6 @@ class SDNCheckTests(TestCase):
content_type='application/json'
)
def assert_sdn_check_failure_recorded(self, response):
""" Assert an SDN check failure is logged and has the correct values. """
self.assertEqual(SDNCheckFailure.objects.count(), 1)
sdn_object = SDNCheckFailure.objects.first()
self.assertEqual(sdn_object.full_name, self.name)
self.assertEqual(sdn_object.country, self.country)
self.assertEqual(sdn_object.site, self.site_configuration.site)
self.assertEqual(sdn_object.sdn_check_response, response)
@httpretty.activate
@override_settings(SDN_CHECK_REQUEST_TIMEOUT=0.1)
def test_sdn_check_timeout(self):
......@@ -99,7 +93,7 @@ class SDNCheckTests(TestCase):
self.mock_sdn_response(mock_timeout, status_code=200)
with self.assertRaises(Timeout):
with mock.patch('ecommerce.extensions.payment.utils.logger.exception') as mock_logger:
self.sdn_validator.search(self.name, self.country)
self.sdn_validator.search(self.name, self.address, self.country)
self.assertTrue(mock_logger.called)
@httpretty.activate
......@@ -108,7 +102,7 @@ class SDNCheckTests(TestCase):
self.mock_sdn_response(json.dumps({'total': 1}), status_code=400)
with self.assertRaises(HTTPError):
with mock.patch('ecommerce.extensions.payment.utils.logger.exception') as mock_logger:
self.sdn_validator.search(self.name, self.country)
self.sdn_validator.search(self.name, self.address, self.country)
self.assertTrue(mock_logger.called)
@httpretty.activate
......@@ -116,19 +110,35 @@ class SDNCheckTests(TestCase):
""" Verify the SDN check returns the number of matches and records the match. """
sdn_response = {'total': 1}
self.mock_sdn_response(json.dumps(sdn_response))
response = self.sdn_validator.search(self.name, self.country)
response = self.sdn_validator.search(self.name, self.address, self.country)
self.assertEqual(response, sdn_response)
def test_deactivate_user(self):
""" Verify an SDN failure is logged. """
response = {'description': 'Bad dude.'}
product1 = factories.ProductFactory(stockrecords__partner__short_code='first')
product2 = factories.ProductFactory(stockrecords__partner__short_code='second')
basket = factories.BasketFactory(owner=self.user, site=self.site_configuration.site)
basket.add(product1)
basket.add(product2)
self.assertEqual(SDNCheckFailure.objects.count(), 0)
with mock.patch.object(User, 'deactivate_account') as deactivate_account:
deactivate_account.return_value = True
self.sdn_validator.deactivate_user(
self.user,
self.site_configuration,
basket,
self.name,
self.address,
self.country,
response)
self.assert_sdn_check_failure_recorded(response)
response
)
self.assertEqual(SDNCheckFailure.objects.count(), 1)
sdn_object = SDNCheckFailure.objects.first()
self.assertEqual(sdn_object.full_name, self.name)
self.assertEqual(sdn_object.address, self.address)
self.assertEqual(sdn_object.country, self.country)
self.assertEqual(sdn_object.site, self.site_configuration.site)
self.assertEqual(sdn_object.sdn_check_response, response)
self.assertEqual(sdn_object.products.count(), basket.lines.count())
self.assertIn(product1, sdn_object.products.all())
self.assertIn(product2, sdn_object.products.all())
......@@ -73,7 +73,7 @@ class SDNClient(object):
self.api_key = api_key
self.sdn_list = sdn_list
def search(self, name, country):
def search(self, name, address, country):
"""
Searches the OFAC list for an individual with the specified details.
The check returns zero hits if:
......@@ -83,6 +83,7 @@ class SDNClient(object):
Args:
name (str): Individual's full name.
address (str): Individual's address.
country (str): ISO 3166-1 alpha-2 country code where the individual is from.
Returns:
dict: SDN API response.
......@@ -93,6 +94,7 @@ class SDNClient(object):
'api_key': self.api_key,
'type': 'individual',
'name': name,
'address': address,
'countries': country
})
sdn_check_url = '{api_url}?{params}'.format(
......@@ -115,23 +117,28 @@ class SDNClient(object):
return json.loads(response.content)
def deactivate_user(self, user, site_configuration, name, country, search_results):
def deactivate_user(self, basket, name, address, country, search_results):
""" Deactivates a user account.
Args:
user (User): User whose account should be deactivated.
site_configuration (SiteConfiguration): The current site's configuration.
basket (Basket): The user's basket.
name (str): The user's name.
address (str): The user's address.
country (str): ISO 3166-1 alpha-2 country code where the individual is from.
search_results (dict): Results from a call to `search` that will
be recorded as the reason for the deactivation.
"""
SDNCheckFailure.objects.create(
site = basket.site
snd_failure = SDNCheckFailure.objects.create(
full_name=name,
username=user.username,
username=basket.owner.username,
address=address,
country=country,
site=site_configuration.site,
site=site,
sdn_check_response=search_results
)
logger.warning('SDN check failed for user [%s] on site [%s]', name, site_configuration.site.name)
user.deactivate_account(site_configuration)
for line in basket.lines.all():
snd_failure.products.add(line.product)
logger.warning('SDN check failed for user [%s] on site [%s]', name, site.name)
basket.owner.deactivate_account(site.siteconfiguration)
......@@ -169,6 +169,8 @@ define([
function sdnCheck(event) {
var first_name = $('input[name=first_name]').val(),
last_name = $('input[name=last_name]').val(),
address = $('input[name=address_line1]').val(),
city = $('input[name=city]').val(),
country = $('select[name=country]').val();
$.ajax({
......@@ -181,6 +183,7 @@ define([
},
data: JSON.stringify({
'name': _s.sprintf('%s %s', first_name, last_name),
'address': _s.sprintf('%s, %s', address, city),
'country': country
}),
async: false,
......
define([
'jquery',
'underscore',
'underscore.string',
'pages/basket_page',
'utils/utils',
'utils/analytics_utils',
......@@ -12,6 +13,7 @@ define([
],
function ($,
_,
_s,
BasketPage,
Utils,
AnalyticsUtils,
......@@ -271,6 +273,8 @@ define([
it('should perform the SDN check', function () {
var first_name = 'Darth',
last_name = 'Vader',
address = 'Deck 1',
city = 'Death Star',
country = 'DS',
args,
ajaxData,
......@@ -279,6 +283,8 @@ define([
$('input[name=first_name]').val(first_name);
$('input[name=last_name]').val(last_name);
$('input[name=address_line1]').val(address);
$('input[name=city]').val(city);
$('select[name=country]').val(country);
$('input[name=sdn-check]').val('enabled');
......@@ -297,7 +303,8 @@ define([
expect(args.method).toEqual('POST');
expect(args.url).toEqual('/api/v2/sdn/search/');
expect(args.contentType).toEqual('application/json; charset=utf-8');
expect(ajaxData.name).toEqual('Darth Vader');
expect(ajaxData.name).toEqual(_s.sprintf('%s %s', first_name, last_name));
expect(ajaxData.address).toEqual(_s.sprintf('%s, %s', address, city));
expect(ajaxData.country).toEqual(country);
});
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment