Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
ecommerce
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
ecommerce
Commits
ee21bc1e
Commit
ee21bc1e
authored
Feb 16, 2017
by
Vedran Karacic
Committed by
Vedran Karačić
Feb 27, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add address and products to SDN failure model.
parent
48700115
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
92 additions
and
30 deletions
+92
-30
ecommerce/extensions/api/v2/tests/views/test_sdn.py
+1
-0
ecommerce/extensions/api/v2/views/sdn.py
+9
-3
ecommerce/extensions/basket/models.py
+1
-1
ecommerce/extensions/payment/migrations/0016_auto_20170227_1402.py
+26
-0
ecommerce/extensions/payment/models.py
+2
-0
ecommerce/extensions/payment/tests/test_utils.py
+26
-16
ecommerce/extensions/payment/utils.py
+16
-9
ecommerce/static/js/pages/basket_page.js
+3
-0
ecommerce/static/js/test/specs/pages/basket_page_spec.js
+8
-1
No files found.
ecommerce/extensions/api/v2/tests/views/test_sdn.py
View file @
ee21bc1e
...
...
@@ -29,6 +29,7 @@ class SDNCheckViewSetTests(TestCase):
self
.
PATH
,
data
=
json
.
dumps
({
'name'
:
'Tester'
,
'address'
:
'Testlandia'
,
'country'
:
'TE'
}),
content_type
=
JSON_CONTENT_TYPE
...
...
ecommerce/extensions/api/v2/views/sdn.py
View file @
ee21bc1e
"""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
)
...
...
ecommerce/extensions/basket/models.py
View file @
ee21bc1e
...
...
@@ -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
:
...
...
ecommerce/extensions/payment/migrations/0016_auto_20170227_1402.py
0 → 100644
View file @
ee21bc1e
# -*- 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'
),
),
]
ecommerce/extensions/payment/models.py
View file @
ee21bc1e
...
...
@@ -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
):
...
...
ecommerce/extensions/payment/tests/test_utils.py
View file @
ee21bc1e
...
...
@@ -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
())
ecommerce/extensions/payment/utils.py
View file @
ee21bc1e
...
...
@@ -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
)
ecommerce/static/js/pages/basket_page.js
View file @
ee21bc1e
...
...
@@ -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
,
...
...
ecommerce/static/js/test/specs/pages/basket_page_spec.js
View file @
ee21bc1e
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
);
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment