-
-
Notifications
You must be signed in to change notification settings - Fork 775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
compliance - blocked country and user list #5602
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from django.contrib import admin | ||
|
||
from .models import Country, Entity | ||
|
||
|
||
class GeneralAdmin(admin.ModelAdmin): | ||
ordering = ['-id'] | ||
list_display = ['created_on', '__str__'] | ||
|
||
admin.site.register(Country, GeneralAdmin) | ||
admin.site.register(Entity, GeneralAdmin) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class ComplianceConfig(AppConfig): | ||
name = 'compliance' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
''' | ||
Copyright (C) 2019 Gitcoin Core | ||
|
||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU Affero General Public License as published | ||
by the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
|
||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU Affero General Public License for more details. | ||
|
||
You should have received a copy of the GNU Affero General Public License | ||
along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
''' | ||
|
||
import urllib.request | ||
import xml.etree as etree | ||
import xml.etree.ElementTree as ET | ||
|
||
from django.core.management.base import BaseCommand | ||
from django.db import transaction | ||
|
||
from compliance.models import Country, Entity | ||
|
||
|
||
def insert_countries(): | ||
# clear existing table | ||
Country.objects.all().delete() | ||
|
||
# pull data | ||
countries = 'Balkans, Belarus, Burma, Cote D\'Ivoire , Cuba, Democratic Republic of Congo, Iran, Iraq, Liberia, North Korea, Sudan, Syria, Zimbabwe'.split(',') | ||
|
||
# insert data | ||
for country in countries: | ||
Country.objects.create(name=country) | ||
|
||
|
||
def insert_entities(): | ||
# clear existing table | ||
Entity.objects.all().delete() | ||
|
||
# pull data | ||
url = 'https://www.treasury.gov/ofac/downloads/consolidated/consolidated.xml' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. who gave this this btw ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i dont understand the question? |
||
response = urllib.request.urlopen(url).read() | ||
tree = ET.fromstring(response) | ||
|
||
# insert data | ||
for ele in tree: | ||
try: | ||
response = {} | ||
keys = ['firstName', 'lastName', 'sdnType', 'city', 'country', 'program', 'stateOrProvince', 'uid'] | ||
for key in keys: | ||
elements = ele.findall('{http://tempuri.org/sdnList.xsd}' + f'{key}') | ||
element = elements[0].text if len(elements) else '' | ||
response[key] = element | ||
response['fullName'] = (response.get('firstName', '') + ' ' + response.get('lastName', '')).strip() | ||
Entity.objects.create(**response) | ||
except Exception as e: | ||
print(e) | ||
|
||
|
||
class Command(BaseCommand): | ||
|
||
help = 'syncs compliance info from remote server' | ||
|
||
def handle(self, *args, **options): | ||
with transaction.atomic(): | ||
insert_countries() | ||
insert_entities() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# Generated by Django 2.2.4 on 2019-12-05 20:47 | ||
|
||
from django.db import migrations, models | ||
import economy.models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Country', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('created_on', models.DateTimeField(db_index=True, default=economy.models.get_time)), | ||
('modified_on', models.DateTimeField(default=economy.models.get_time)), | ||
('name', models.CharField(max_length=500)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name='Entity', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('created_on', models.DateTimeField(db_index=True, default=economy.models.get_time)), | ||
('modified_on', models.DateTimeField(default=economy.models.get_time)), | ||
('firstName', models.CharField(max_length=500)), | ||
('lastName', models.CharField(max_length=500)), | ||
('fullName', models.CharField(max_length=500)), | ||
('sdnType', models.CharField(max_length=500)), | ||
('city', models.CharField(max_length=500)), | ||
('country', models.CharField(max_length=500)), | ||
('program', models.CharField(max_length=500)), | ||
('stateOrProvince', models.CharField(max_length=500)), | ||
('uid', models.CharField(max_length=25)), | ||
], | ||
options={ | ||
'abstract': False, | ||
}, | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from django.db import models | ||
|
||
from economy.models import SuperModel | ||
|
||
|
||
class Entity(SuperModel): | ||
firstName = models.CharField(max_length=500) | ||
lastName = models.CharField(max_length=500) | ||
fullName = models.CharField(max_length=500) | ||
sdnType = models.CharField(max_length=500) | ||
city = models.CharField(max_length=500) | ||
country = models.CharField(max_length=500) | ||
program = models.CharField(max_length=500) | ||
stateOrProvince = models.CharField(max_length=500) | ||
uid = models.CharField(max_length=25) | ||
|
||
def __str__(self): | ||
"""Return the string representation of this obj.""" | ||
return f'{self.fullName}' | ||
|
||
|
||
class Country(SuperModel): | ||
name = models.CharField(max_length=500) | ||
|
||
def __str__(self): | ||
"""Return the string representation of this obj.""" | ||
return f'{self.name}' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.test import TestCase | ||
|
||
# Create your tests here. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.shortcuts import render | ||
|
||
# Create your views here. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -801,7 +801,31 @@ def get_tx_status(txid, network, created_on): | |
|
||
|
||
def is_blocked(handle): | ||
return BlockedUser.objects.filter(handle__iexact=handle, active=True).exists() | ||
# check admin block list | ||
is_on_blocked_list = BlockedUser.objects.filter(handle__iexact=handle, active=True).exists() | ||
if is_on_blocked_list: | ||
return True | ||
|
||
# check banned country list | ||
from compliance.models import Country, Entity | ||
owocki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
last_login = self.actions.filter(action='Login').order_by('pk').last() | ||
if last_login: | ||
last_login_country = last_login.location_data.get('country_name') | ||
if last_login_country: | ||
is_on_banned_countries = Country.objects.filter(name=last_login_country) | ||
if is_on_banned_countries: | ||
return True | ||
|
||
# check banned entity list | ||
if self.user: | ||
first_name = self.user.first_name | ||
last_name = self.user.last_name | ||
full_name = '{first_name} {last_name}' | ||
is_on_banned_user_list = Entitty.objects.filter(fullName__icontains=full_name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
if is_on_banned_user_list: | ||
return True | ||
|
||
return False | ||
|
||
|
||
def get_nonce(network, address): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to ever do any kind of tracking / recording of compliance related service denial or anything like that we'll have to not rewrite this table over and over - a premature optimization for now but lets all make sure we are aware of this for the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it; @alexvotofuture should we keep a record of OFAC denials? would it be handy to have to show regulatory compliance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets punt to a v2