update deps & code format lambdainst

master
alice 3 years ago
parent afebc151d0
commit a7258ca37d

@ -15,18 +15,29 @@ from payments.admin import UserCouponInline, UserLedgerInline
def make_user_link(user): def make_user_link(user):
change_url = resolve_url('admin:auth_user_change', user.id) change_url = resolve_url("admin:auth_user_change", user.id)
return format_html('<a href="{}">{}</a>', change_url, user.username) return format_html('<a href="{}">{}</a>', change_url, user.username)
class VPNUserInline(admin.StackedInline): class VPNUserInline(admin.StackedInline):
model = VPNUser model = VPNUser
can_delete = False can_delete = False
fk_name = 'user' fk_name = "user"
fields = ('notes', 'expiration', 'last_expiry_notice', 'notify_expiration', fields = (
'last_vpn_auth_fail_notice', 'notify_vpn_auth_fail', "notes",
'trial_periods_given', 'referrer_a', 'campaign', 'last_vpn_auth', 'last_core_sync') "expiration",
readonly_fields = ('referrer_a', 'last_vpn_auth', 'last_core_sync', 'campaign') "last_expiry_notice",
"notify_expiration",
"last_vpn_auth_fail_notice",
"notify_vpn_auth_fail",
"trial_periods_given",
"referrer_a",
"campaign",
"last_vpn_auth",
"last_core_sync",
)
readonly_fields = ("referrer_a", "last_vpn_auth", "last_core_sync", "campaign")
def referrer_a(self, object): def referrer_a(self, object):
if not object.referrer: if not object.referrer:
@ -38,40 +49,58 @@ class VPNUserInline(admin.StackedInline):
else: else:
s += _("(not rewarded)") s += _("(not rewarded)")
return s return s
referrer_a.allow_tags = True referrer_a.allow_tags = True
referrer_a.short_description = _("Referrer") referrer_a.short_description = _("Referrer")
def is_paid(self, object): def is_paid(self, object):
return object.is_paid return object.is_paid
is_paid.boolean = True is_paid.boolean = True
is_paid.short_description = _("Is paid?") is_paid.short_description = _("Is paid?")
class UserAdmin(UserAdmin): class UserAdmin(UserAdmin):
inlines = (VPNUserInline, UserLedgerInline, UserCouponInline) inlines = (VPNUserInline, UserLedgerInline, UserCouponInline)
list_display = ('username', 'email', 'is_staff', 'date_joined', 'is_paid') list_display = ("username", "email", "is_staff", "date_joined", "is_paid")
ordering = ('-date_joined', ) ordering = ("-date_joined",)
fieldsets = ( fieldsets = (
(None, {'fields': ('username', 'password', 'email', 'links')}), (None, {"fields": ("username", "password", "email", "links")}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}), (_("Important dates"), {"fields": ("last_login", "date_joined")}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', (
'groups', 'user_permissions')}), _("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
)
},
),
) )
readonly_fields = ('last_login', 'date_joined', 'links') readonly_fields = ("last_login", "date_joined", "links")
actions = (django_lcore.core_sync_action, ) actions = (django_lcore.core_sync_action,)
def is_paid(self, object): def is_paid(self, object):
return object.vpnuser.is_paid return object.vpnuser.is_paid
is_paid.boolean = True is_paid.boolean = True
is_paid.short_description = _("Is paid?") is_paid.short_description = _("Is paid?")
def links(self, object): def links(self, object):
payments_url = resolve_url('admin:payments_payment_changelist') payments_url = resolve_url("admin:payments_payment_changelist")
tickets_url = resolve_url('admin:tickets_ticket_changelist') tickets_url = resolve_url("admin:tickets_ticket_changelist")
fmt = '<a href="{}?user__id__exact={}">{}</a>' fmt = '<a href="{}?user__id__exact={}">{}</a>'
return format_html(fmt + " - " + fmt, return format_html(
payments_url, object.id, "Payments", fmt + " - " + fmt,
tickets_url, object.id, "Tickets", payments_url,
object.id,
"Payments",
tickets_url,
object.id,
"Tickets",
) )
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):

@ -7,14 +7,14 @@ from django.utils.safestring import mark_safe
class FormPureRender: class FormPureRender:
def as_pure_aligned(self): def as_pure_aligned(self):
html = '' html = ""
for f in self: for f in self:
html += '<div class="pure-control-group">\n' html += '<div class="pure-control-group">\n'
html += str(f.label_tag()) + '\n' html += str(f.label_tag()) + "\n"
html += str(f) + '\n' html += str(f) + "\n"
if f.errors: if f.errors:
html += str(f.errors) + '\n' html += str(f.errors) + "\n"
html += '</div>\n' html += "</div>\n"
return mark_safe(html) return mark_safe(html)
@ -30,36 +30,43 @@ class UserField(forms.RegexField):
class SignupForm(forms.Form, FormPureRender): class SignupForm(forms.Form, FormPureRender):
username = UserField( username = UserField(
label=_("Username"), min_length=2, max_length=16, regex='^[a-zA-Z0-9_-]+$', label=_("Username"),
widget=forms.TextInput(attrs={'required': 'true', min_length=2,
'pattern': '[a-zA-Z0-9_-]{2,32}', max_length=16,
'placeholder': _("Username"), regex="^[a-zA-Z0-9_-]+$",
'autofocus': 'true'}) widget=forms.TextInput(
attrs={
"required": "true",
"pattern": "[a-zA-Z0-9_-]{2,32}",
"placeholder": _("Username"),
"autofocus": "true",
}
),
) )
password = forms.CharField( password = forms.CharField(
label=_("Password"), label=_("Password"),
widget=forms.PasswordInput(attrs={'placeholder': _("Anything")}) widget=forms.PasswordInput(attrs={"placeholder": _("Anything")}),
) )
password2 = forms.CharField( password2 = forms.CharField(
label=_("Repeat"), label=_("Repeat"),
widget=forms.PasswordInput(attrs={'placeholder': _("Same Anything")}) widget=forms.PasswordInput(attrs={"placeholder": _("Same Anything")}),
) )
email = forms.EmailField( email = forms.EmailField(
label=_("E-Mail"), label=_("E-Mail"),
widget=forms.EmailInput(attrs={'placeholder': _("E-Mail")}), widget=forms.EmailInput(attrs={"placeholder": _("E-Mail")}),
required=False, required=False,
) )
def clean_password(self): def clean_password(self):
if self.data['password'] != self.data['password2']: if self.data["password"] != self.data["password2"]:
raise forms.ValidationError(_("Passwords are not the same")) raise forms.ValidationError(_("Passwords are not the same"))
return self.data['password'] return self.data["password"]
class ReqEmailForm(forms.Form, FormPureRender): class ReqEmailForm(forms.Form, FormPureRender):
email = forms.EmailField( email = forms.EmailField(
label=_("E-Mail"), label=_("E-Mail"),
widget=forms.EmailInput(attrs={'placeholder': _("E-Mail")}), widget=forms.EmailInput(attrs={"placeholder": _("E-Mail")}),
) )
@ -73,7 +80,11 @@ def wg_pk_validator(s):
class WgPeerForm(forms.Form): class WgPeerForm(forms.Form):
public_key = forms.CharField(min_length=3, max_length=100, strip=True, required=False, validators=[ public_key = forms.CharField(
wg_pk_validator min_length=3,
]) max_length=100,
strip=True,
required=False,
validators=[wg_pk_validator],
)
name = forms.CharField(max_length=21, required=False) name = forms.CharField(max_length=21, required=False)

@ -8,8 +8,8 @@ from payments.models import Payment
PERIOD_VERBOSE_NAME = { PERIOD_VERBOSE_NAME = {
'y': "per month", "y": "per month",
'm': "per day", "m": "per day",
} }
@ -18,9 +18,23 @@ def monthdelta(date, delta):
y = date.year + (date.month + delta - 1) // 12 y = date.year + (date.month + delta - 1) // 12
if not m: if not m:
m = 12 m = 12
d = min(date.day, [31, 29 if y % 4 == 0 and not y % 400 == 0 d = min(
else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 date.day,
][m - 1]) [
31,
29 if y % 4 == 0 and not y % 400 == 0 else 28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,
][m - 1],
)
return date.replace(day=d, month=m, year=y) return date.replace(day=d, month=m, year=y)
@ -38,57 +52,64 @@ def last_months(n=12):
def time_filter_future(period, m, df): def time_filter_future(period, m, df):
def _filter(o): def _filter(o):
if period == 'm': if period == "m":
return df(o).date() <= m return df(o).date() <= m
if period == 'y': if period == "y":
return df(o).date().replace(day=1) <= m return df(o).date().replace(day=1) <= m
return _filter return _filter
def time_filter_between(period, m, df): def time_filter_between(period, m, df):
def _filter(o): def _filter(o):
if period == 'm': if period == "m":
return df(o).year == m.year and df(o).month == m.month and df(o).day == m.day return (
df(o).year == m.year and df(o).month == m.month and df(o).day == m.day
)
return df(o).date() <= m and df(o).date() > (m - timedelta(days=1)) return df(o).date() <= m and df(o).date() > (m - timedelta(days=1))
if period == 'y': if period == "y":
return df(o).year == m.year and df(o).month == m.month return df(o).year == m.year and df(o).month == m.month
return (df(o).date().replace(day=1) <= m and return df(o).date().replace(day=1) <= m and df(o).date().replace(day=1) > (
df(o).date().replace(day=1) > (m - timedelta(days=30))) m - timedelta(days=30)
)
return _filter return _filter
def users_graph(period): def users_graph(period):
chart = pygal.Line(fill=True, x_label_rotation=75, show_legend=False) chart = pygal.Line(fill=True, x_label_rotation=75, show_legend=False)
chart.title = 'Users %s' % PERIOD_VERBOSE_NAME[period] chart.title = "Users %s" % PERIOD_VERBOSE_NAME[period]
chart.x_labels = [] chart.x_labels = []
values = [] values = []
gen = last_days(30) if period == 'm' else last_months(12) gen = last_days(30) if period == "m" else last_months(12)
users = User.objects.all() users = User.objects.all()
for m in gen: for m in gen:
filter_ = time_filter_future(period, m, lambda o: o.date_joined) filter_ = time_filter_future(period, m, lambda o: o.date_joined)
users_filtered = filter(filter_, users) users_filtered = filter(filter_, users)
values.append(len(list(users_filtered))) values.append(len(list(users_filtered)))
chart.x_labels.append('%02d/%02d' % (m.month, m.day)) chart.x_labels.append("%02d/%02d" % (m.month, m.day))
chart.add('Users', values) chart.add("Users", values)
return chart.render() return chart.render()
def payments_paid_graph(period): def payments_paid_graph(period):
chart = pygal.StackedBar(x_label_rotation=75, show_legend=True) chart = pygal.StackedBar(x_label_rotation=75, show_legend=True)
chart.x_labels = [] chart.x_labels = []
gen = list(last_days(30) if period == 'm' else last_months(12)) gen = list(last_days(30) if period == "m" else last_months(12))
chart.title = 'Payments %s in €' % (PERIOD_VERBOSE_NAME[period]) chart.title = "Payments %s in €" % (PERIOD_VERBOSE_NAME[period])
for m in gen: for m in gen:
chart.x_labels.append('%02d/%02d' % (m.month, m.day)) chart.x_labels.append("%02d/%02d" % (m.month, m.day))
values = dict() values = dict()
for backend_id, backend in BACKENDS.items(): for backend_id, backend in BACKENDS.items():
values = [] values = []
payments = list(Payment.objects.filter(status='confirmed', backend_id=backend_id)) payments = list(
Payment.objects.filter(status="confirmed", backend_id=backend_id)
)
for m in gen: for m in gen:
filter_ = time_filter_between(period, m, lambda o: o.created) filter_ = time_filter_between(period, m, lambda o: o.created)
@ -103,17 +124,19 @@ def payments_paid_graph(period):
def payments_success_graph(period): def payments_success_graph(period):
chart = pygal.StackedBar(x_label_rotation=75, show_legend=True) chart = pygal.StackedBar(x_label_rotation=75, show_legend=True)
chart.x_labels = [] chart.x_labels = []
gen = list(last_days(30) if period == 'm' else last_months(12)) gen = list(last_days(30) if period == "m" else last_months(12))
chart.title = 'Successful payments %s' % (PERIOD_VERBOSE_NAME[period]) chart.title = "Successful payments %s" % (PERIOD_VERBOSE_NAME[period])
for m in gen: for m in gen:
chart.x_labels.append('%02d/%02d' % (m.month, m.day)) chart.x_labels.append("%02d/%02d" % (m.month, m.day))
values = dict() values = dict()
for backend_id, backend in BACKENDS.items(): for backend_id, backend in BACKENDS.items():
values = [] values = []
payments = list(Payment.objects.filter(status='confirmed', backend_id=backend_id)) payments = list(
Payment.objects.filter(status="confirmed", backend_id=backend_id)
)
for m in gen: for m in gen:
filter_ = time_filter_between(period, m, lambda o: o.created) filter_ = time_filter_between(period, m, lambda o: o.created)
@ -123,4 +146,3 @@ def payments_success_graph(period):
chart.add(backend_id, values) chart.add(backend_id, values)
return chart.render() return chart.render()

@ -8,10 +8,10 @@ from .models import User
class ReferrerMiddleware(MiddlewareMixin): class ReferrerMiddleware(MiddlewareMixin):
def process_request(self, request): def process_request(self, request):
if 'ref' in request.GET: if "ref" in request.GET:
id = request.GET['ref'] id = request.GET["ref"]
elif 'referrer' in request.COOKIES: elif "referrer" in request.COOKIES:
id = request.COOKIES['referrer'] id = request.COOKIES["referrer"]
else: else:
return return
@ -25,33 +25,36 @@ class ReferrerMiddleware(MiddlewareMixin):
except User.DoesNotExist: except User.DoesNotExist:
return return
request.session['referrer'] = u.id request.session["referrer"] = u.id
def process_response(self, request, response): def process_response(self, request, response):
id = request.session.get('referrer') id = request.session.get("referrer")
if not id: if not id:
return response return response
max_age = 365 * 24 * 60 * 60 max_age = 365 * 24 * 60 * 60
expires = (datetime.utcnow() + timedelta(seconds=max_age)) expires = datetime.utcnow() + timedelta(seconds=max_age)
expires = expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT") expires = expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT")
response.set_cookie('referrer', id, response.set_cookie(
"referrer",
id,
max_age=max_age, max_age=max_age,
expires=expires, expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN, domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None) secure=settings.SESSION_COOKIE_SECURE or None,
)
return response return response
class CampaignMiddleware(MiddlewareMixin): class CampaignMiddleware(MiddlewareMixin):
GET_FIELDS = ['pk_campaign', 'utm_campaign', 'utm_medium', 'utm_source'] GET_FIELDS = ["pk_campaign", "utm_campaign", "utm_medium", "utm_source"]
def _get_name(self, request): def _get_name(self, request):
for f in self.GET_FIELDS: for f in self.GET_FIELDS:
if f in request.GET and request.GET[f]: if f in request.GET and request.GET[f]:
return request.GET[f] return request.GET[f]
if 'campaign' in request.COOKIES: if "campaign" in request.COOKIES:
return request.COOKIES['campaign'] return request.COOKIES["campaign"]
return None return None
def process_request(self, request): def process_request(self, request):
@ -61,22 +64,25 @@ class CampaignMiddleware(MiddlewareMixin):
name = name.strip() name = name.strip()
if len(name) >= 64 or not re.match('^[a-zA-Z0-9_.:-]+$', name): if len(name) >= 64 or not re.match("^[a-zA-Z0-9_.:-]+$", name):
return return
request.session['campaign'] = name request.session["campaign"] = name
def process_response(self, request, response): def process_response(self, request, response):
name = request.session.get('campaign') name = request.session.get("campaign")
if not name: if not name:
return response return response
max_age = 365 * 24 * 60 * 60 max_age = 365 * 24 * 60 * 60
expires = (datetime.utcnow() + timedelta(seconds=max_age)) expires = datetime.utcnow() + timedelta(seconds=max_age)
expires = expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT") expires = expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT")
response.set_cookie('campaign', name, response.set_cookie(
"campaign",
name,
max_age=max_age, max_age=max_age,
expires=expires, expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN, domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None) secure=settings.SESSION_COOKIE_SECURE or None,
)
return response return response

@ -2,7 +2,11 @@ import random
from django.db import models, IntegrityError from django.db import models, IntegrityError
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django_lcore.core import LcoreUserProfileMethods, setup_sync_hooks, VPN_AUTH_STORAGE from django_lcore.core import (
LcoreUserProfileMethods,
setup_sync_hooks,
VPN_AUTH_STORAGE,
)
from payments.models import BaseSubUser from payments.models import BaseSubUser
@ -11,7 +15,7 @@ prng = random.SystemRandom()
def random_gift_code(): def random_gift_code():
charset = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ" charset = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ"
return ''.join([prng.choice(charset) for n in range(10)]) return "".join([prng.choice(charset) for n in range(10)])
class VPNUser(models.Model, BaseSubUser, LcoreUserProfileMethods): class VPNUser(models.Model, BaseSubUser, LcoreUserProfileMethods):
@ -33,8 +37,9 @@ class VPNUser(models.Model, BaseSubUser, LcoreUserProfileMethods):
last_vpn_auth = models.DateTimeField(blank=True, null=True) last_vpn_auth = models.DateTimeField(blank=True, null=True)
last_core_sync = models.DateTimeField(blank=True, null=True) last_core_sync = models.DateTimeField(blank=True, null=True)
referrer = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL, referrer = models.ForeignKey(
related_name='referrals') User, blank=True, null=True, on_delete=models.SET_NULL, related_name="referrals"
)
referrer_used = models.BooleanField(default=False) referrer_used = models.BooleanField(default=False)
campaign = models.CharField(blank=True, null=True, max_length=64) campaign = models.CharField(blank=True, null=True, max_length=64)
@ -49,15 +54,20 @@ class VPNUser(models.Model, BaseSubUser, LcoreUserProfileMethods):
def on_payment_confirmed(self, payment): def on_payment_confirmed(self, payment):
if self.referrer and not self.referrer_used: if self.referrer and not self.referrer_used:
self.referrer.vpnuser.add_paid_months(1, 'referrer', f"rewarded for {self.user.username} (payment #{payment.id})") self.referrer.vpnuser.add_paid_months(
1,
"referrer",
f"rewarded for {self.user.username} (payment #{payment.id})",
)
self.referrer.vpnuser.save() self.referrer.vpnuser.save()
self.referrer_used = True self.referrer_used = True
self.save() self.save()
def lcore_sync(self): def lcore_sync(self):
if VPN_AUTH_STORAGE == 'inst': if VPN_AUTH_STORAGE == "inst":
return return
from lambdainst.tasks import push_user from lambdainst.tasks import push_user
push_user.delay(user_id=self.user.id) push_user.delay(user_id=self.user.id)
def notify_payment(self, payment): def notify_payment(self, payment):
@ -72,12 +82,13 @@ class VPNUser(models.Model, BaseSubUser, LcoreUserProfileMethods):
def __str__(self): def __str__(self):
return self.user.username return self.user.username
setup_sync_hooks(User, VPNUser) setup_sync_hooks(User, VPNUser)
#from django.db.models.signals import post_save # from django.db.models.signals import post_save
#from django.dispatch import receiver # from django.dispatch import receiver
#@receiver(post_save, sender=User) # @receiver(post_save, sender=User)
#def create_vpnuser(sender, instance, created, **kwargs): # def create_vpnuser(sender, instance, created, **kwargs):
# if created: # if created:
# try: # try:
# VPNUser.objects.create(user=instance) # VPNUser.objects.create(user=instance)

@ -7,83 +7,86 @@ from django.conf import settings
CA_CERT = settings.OPENVPN_CA CA_CERT = settings.OPENVPN_CA
CONFIG_OS = ( CONFIG_OS = (
('windows', _("Windows")), ("windows", _("Windows")),
('android', _("Android")), ("android", _("Android")),
('ubuntu', _("Ubuntu")), ("ubuntu", _("Ubuntu")),
('osx', _("OS X")), ("osx", _("OS X")),
('ios', _("iOS")), ("ios", _("iOS")),
('chromeos', _("Chrome OS")), ("chromeos", _("Chrome OS")),
('freebox', _("Freebox")), ("freebox", _("Freebox")),
('other', _("Other / GNU/Linux")), ("other", _("Other / GNU/Linux")),
) )
PROTOCOLS = ( PROTOCOLS = (
('udp', _("UDP (default)")), ("udp", _("UDP (default)")),
('tcp', _("TCP")), ("tcp", _("TCP")),
('udpl', _("UDP (low MTU)")), ("udpl", _("UDP (low MTU)")),
) )
def _make_onc(username, name, hostname, port, protocol, http_proxy=None, ipv6=True): def _make_onc(username, name, hostname, port, protocol, http_proxy=None, ipv6=True):
cert_id = '{%s}' % uuid.uuid4() cert_id = "{%s}" % uuid.uuid4()
vpn_id = '{%s}' % uuid.uuid4() vpn_id = "{%s}" % uuid.uuid4()
openvpn_config = { openvpn_config = {
'ServerCARef': cert_id, "ServerCARef": cert_id,
'ClientCertType': 'None', "ClientCertType": "None",
'CompLZO': 'true', "CompLZO": "true",
'Port': port, "Port": port,
'Proto': protocol, "Proto": protocol,
'ServerPollTimeout': 10, "ServerPollTimeout": 10,
'NsCertType': 'server', "NsCertType": "server",
'Username': username, "Username": username,
} }
cert = { cert = {
'GUID': cert_id, "GUID": cert_id,
'Type': 'Authority', "Type": "Authority",
'X509': CA_CERT.strip(), "X509": CA_CERT.strip(),
} }
vpn = { vpn = {
'GUID': vpn_id, "GUID": vpn_id,
'Name': name, "Name": name,
'Type': 'VPN', "Type": "VPN",
'VPN': { "VPN": {
'Type': 'OpenVPN', "Type": "OpenVPN",
'Host': hostname, "Host": hostname,
'OpenVPN': openvpn_config, "OpenVPN": openvpn_config,
}, },
} }
return json.dumps({ return json.dumps(
'type': 'UnencryptedConfiguration', {
'Certificates': [cert], "type": "UnencryptedConfiguration",
'NetworkConfigurations': [vpn], "Certificates": [cert],
}, indent=2) "NetworkConfigurations": [vpn],
},
indent=2,
)
def make_config(username, gw_name, os, protocol, http_proxy=None, ipv6=True): def make_config(username, gw_name, os, protocol, http_proxy=None, ipv6=True):
use_frag = protocol == 'udpl' and os != 'ios' use_frag = protocol == "udpl" and os != "ios"
ipv6 = ipv6 and (os != 'freebox') ipv6 = ipv6 and (os != "freebox")
http_proxy = http_proxy if protocol == 'tcp' else None http_proxy = http_proxy if protocol == "tcp" else None
resolvconf = os in ('ubuntu', 'other') resolvconf = os in ("ubuntu", "other")
openvpn_proto = {'udp': 'udp', 'udpl': 'udp', 'tcp': 'tcp'} openvpn_proto = {"udp": "udp", "udpl": "udp", "tcp": "tcp"}
openvpn_ports = {'udp': 1196, 'udpl': 1194, 'tcp': 443} openvpn_ports = {"udp": 1196, "udpl": 1194, "tcp": 443}
hostname = 'gw.%s.204vpn.net' % gw_name hostname = "gw.%s.204vpn.net" % gw_name
port = openvpn_ports[protocol] port = openvpn_ports[protocol]
proto = openvpn_proto[protocol] proto = openvpn_proto[protocol]
if os == 'chromeos': if os == "chromeos":
name = "CCrypto VPN" name = "CCrypto VPN"
if gw_name != 'random': if gw_name != "random":
name += " " + gw_name.upper() name += " " + gw_name.upper()
return _make_onc(username, name, hostname, port, proto, http_proxy, ipv6) return _make_onc(username, name, hostname, port, proto, http_proxy, ipv6)
remote = str(hostname) remote = str(hostname)
remote += ' ' + str(port) remote += " " + str(port)
remote += ' ' + proto remote += " " + proto
config = """\ config = """\
# +----------------------------+ # +----------------------------+
@ -106,9 +109,11 @@ remote {remote}
auth-user-pass auth-user-pass
""".format(remote=remote) """.format(
remote=remote
)
if os == 'ios': if os == "ios":
# i'd like to note here how much i hate OpenVPN # i'd like to note here how much i hate OpenVPN
config += "redirect-gateway ipv6\n" config += "redirect-gateway ipv6\n"
config += 'push "route 0.0.0.0 128.0.0.0"\n' config += 'push "route 0.0.0.0 128.0.0.0"\n'
@ -133,16 +138,13 @@ auth-user-pass
config += "down /etc/openvpn/update-resolv-conf\n" config += "down /etc/openvpn/update-resolv-conf\n"
config += "\n" config += "\n"
if os == 'windows': if os == "windows":
config += "register-dns\n" config += "register-dns\n"
config += "\n" config += "\n"
config += "<ca>\n%s\n</ca>" % CA_CERT config += "<ca>\n%s\n</ca>" % CA_CERT
if os == 'windows': if os == "windows":
config = config.replace('\n', '\r\n') config = config.replace("\n", "\r\n")
return config return config

@ -69,7 +69,7 @@ def notify_vpn_auth_fails():
NOTIFY_EVERY = timedelta(days=180) # users notified recently should be ignored NOTIFY_EVERY = timedelta(days=180) # users notified recently should be ignored
def find_fails_for_user(u): def find_fails_for_user(u):
""" returns true if there are 'many' recent fails """ """returns true if there are 'many' recent fails"""
path = api.info["current_instance"] + "/users/" + u.username + "/auth-events/" path = api.info["current_instance"] + "/users/" + u.username + "/auth-events/"
try: try:
r = api.get(path) r = api.get(path)
@ -181,7 +181,7 @@ def notify_vpn_auth_fails():
@shared_task @shared_task
def notify_account_expiration(): def notify_account_expiration():
""" Notify users near the end of their subscription """ """Notify users near the end of their subscription"""
from_email = settings.DEFAULT_FROM_EMAIL from_email = settings.DEFAULT_FROM_EMAIL
for v in parse_integer_list(site_config.NOTIFY_DAYS_BEFORE): for v in parse_integer_list(site_config.NOTIFY_DAYS_BEFORE):
@ -213,7 +213,7 @@ def notify_account_expiration():
def get_next_expirations(days=3): def get_next_expirations(days=3):
""" Gets users whose subscription will expire in some days """ """Gets users whose subscription will expire in some days"""
limit_date = timezone.now() + timedelta(days=days) limit_date = timezone.now() + timedelta(days=days)

@ -9,11 +9,10 @@ register = template.Library()
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def active(context, pattern_or_urlname): def active(context, pattern_or_urlname):
try: try:
pattern = '^' + reverse(pattern_or_urlname) pattern = "^" + reverse(pattern_or_urlname)
except NoReverseMatch: except NoReverseMatch:
pattern = pattern_or_urlname pattern = pattern_or_urlname
path = context['request'].path path = context["request"].path
if re.search(pattern, path): if re.search(pattern, path):
return 'active' return "active"
return '' return ""

@ -11,7 +11,7 @@ def bwformat(bps):
try: try:
bps = float(bps) bps = float(bps)
except (TypeError, ValueError, UnicodeDecodeError): except (TypeError, ValueError, UnicodeDecodeError):
value = ungettext("%(bw)d bps", "%(bw)d bps", 0) % {'bw': 0} value = ungettext("%(bw)d bps", "%(bw)d bps", 0) % {"bw": 0}
return avoid_wrapping(value) return avoid_wrapping(value)
filesize_number_format = lambda value: formats.number_format(round(value, 1), -1) filesize_number_format = lambda value: formats.number_format(round(value, 1), -1)
@ -23,7 +23,7 @@ def bwformat(bps):
P = 1 * 10 ** 15 P = 1 * 10 ** 15
if bps < K: if bps < K:
value = ungettext("%(size)d bps", "%(size)d bps", bps) % {'size': bps} value = ungettext("%(size)d bps", "%(size)d bps", bps) % {"size": bps}
elif bps < M: elif bps < M:
value = ugettext("%s Kbps") % filesize_number_format(bps / K) value = ugettext("%s Kbps") % filesize_number_format(bps / K)
elif bps < G: elif bps < G:

@ -103,12 +103,14 @@ class OnlineStripeTests(BaseOnlineTest):
self.selenium.find_element_by_xpath('//button[@type="submit"]').click() self.selenium.find_element_by_xpath('//button[@type="submit"]').click()
self.wait.until( self.wait.until(
EC.presence_of_element_located((By.XPATH, '//*[contains(text(), "Waiting for payment")]')) EC.presence_of_element_located(
(By.XPATH, '//*[contains(text(), "Waiting for payment")]')
)
) )
def check_active(): def check_active():
# refresh payment as we dont have a worker # refresh payment as we dont have a worker
p = Payment.objects.order_by('id').first() p = Payment.objects.order_by("id").first()
p.refresh() p.refresh()
self.selenium.refresh() self.selenium.refresh()
@ -159,12 +161,14 @@ class OnlineStripeTests(BaseOnlineTest):
self.selenium.find_element_by_xpath('//button[@type="submit"]').click() self.selenium.find_element_by_xpath('//button[@type="submit"]').click()
self.wait.until( self.wait.until(
EC.presence_of_element_located((By.XPATH, '//*[contains(text(), "Your subscription is processing.")]')) EC.presence_of_element_located(
(By.XPATH, '//*[contains(text(), "Your subscription is processing.")]')
)
) )
def check_active(): def check_active():
# refresh sub as we dont have a worker # refresh sub as we dont have a worker
p = Subscription.objects.order_by('id').first() p = Subscription.objects.order_by("id").first()
p.refresh() p.refresh()
self.selenium.refresh() self.selenium.refresh()

@ -14,24 +14,24 @@ from payments.models import Payment, Subscription
class UserTestMixin: class UserTestMixin:
def assertRemaining(self, vpnuser, time, delta=5): def assertRemaining(self, vpnuser, time, delta=5):
""" Check that the vpnuser will expire in time (+/- 5 seconds) """ """Check that the vpnuser will expire in time (+/- 5 seconds)"""
exp = vpnuser.expiration or timezone.now() exp = vpnuser.expiration or timezone.now()
seconds = (exp - timezone.now() - time).total_seconds() seconds = (exp - timezone.now() - time).total_seconds()
self.assertAlmostEqual(seconds, 0, delta=delta) self.assertAlmostEqual(seconds, 0, delta=delta)
class UserModelReferrerTest(TestCase, UserTestMixin): class UserModelReferrerTest(TestCase, UserTestMixin):
def setUp(self): def setUp(self):
self.referrer = User.objects.create_user('ref') self.referrer = User.objects.create_user("ref")
self.without_ref = User.objects.create_user('aaaa') self.without_ref = User.objects.create_user("aaaa")
self.with_ref = User.objects.create_user('bbbb') self.with_ref = User.objects.create_user("bbbb")
self.with_ref.vpnuser.referrer = self.referrer self.with_ref.vpnuser.referrer = self.referrer
self.payment = Payment.objects.create( self.payment = Payment.objects.create(
user=self.with_ref, status='confirmed', amount=300, time=timedelta(days=30)) user=self.with_ref, status="confirmed", amount=300, time=timedelta(days=30)
)
def test_no_ref(self): def test_no_ref(self):
self.without_ref.vpnuser.on_payment_confirmed(self.payment) self.without_ref.vpnuser.on_payment_confirmed(self.payment)
@ -40,125 +40,153 @@ class UserModelReferrerTest(TestCase, UserTestMixin):
self.with_ref.vpnuser.on_payment_confirmed(self.payment) self.with_ref.vpnuser.on_payment_confirmed(self.payment)
self.assertTrue(self.with_ref.vpnuser.referrer_used) self.assertTrue(self.with_ref.vpnuser.referrer_used)
self.assertEqual(self.with_ref.vpnuser.referrer, self.referrer) self.assertEqual(self.with_ref.vpnuser.referrer, self.referrer)
self.assertRemaining(self.referrer.vpnuser, timedelta(days=30), delta=24*3600*3) self.assertRemaining(
self.referrer.vpnuser, timedelta(days=30), delta=24 * 3600 * 3
)
class SignupViewTest(TestCase): class SignupViewTest(TestCase):
def test_form(self): def test_form(self):
response = self.client.get('/account/signup') response = self.client.get("/account/signup")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIsInstance(response.context['form'], SignupForm) self.assertIsInstance(response.context["form"], SignupForm)
def test_post(self): def test_post(self):
response = self.client.post('/account/signup', { response = self.client.post(
'username': 'test_un', 'password': 'test_pw', 'password2': 'test_pw'}) "/account/signup",
self.assertRedirects(response, '/account/') {"username": "test_un", "password": "test_pw", "password2": "test_pw"},
)
self.assertRedirects(response, "/account/")
user = User.objects.get(username='test_un') user = User.objects.get(username="test_un")
self.assertTrue(user.check_password('test_pw')) self.assertTrue(user.check_password("test_pw"))
def test_post_error(self): def test_post_error(self):
response = self.client.post('/account/signup', { response = self.client.post(
'username': 'test_un', 'password': 'test_pw', 'password2': 'qsdf'}) "/account/signup",
{"username": "test_un", "password": "test_pw", "password2": "qsdf"},
)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIsInstance(response.context['form'], SignupForm) self.assertIsInstance(response.context["form"], SignupForm)
self.assertFormError(response, 'form', 'password', self.assertFormError(response, "form", "password", "Passwords are not the same")
'Passwords are not the same')
def test_post_referrer(self): def test_post_referrer(self):
ref = User.objects.create_user('referrer') ref = User.objects.create_user("referrer")
response = self.client.post('/account/signup?ref=%d' % ref.id, { response = self.client.post(
'username': 'test_un', 'password': 'test_pw', 'password2': 'test_pw'}) "/account/signup?ref=%d" % ref.id,
self.assertRedirects(response, '/account/') {"username": "test_un", "password": "test_pw", "password2": "test_pw"},
)
self.assertRedirects(response, "/account/")
user = User.objects.get(username='test_un') user = User.objects.get(username="test_un")
self.assertTrue(user.check_password('test_pw')) self.assertTrue(user.check_password("test_pw"))
self.assertEqual(user.vpnuser.referrer, ref) self.assertEqual(user.vpnuser.referrer, ref)
class AccountViewsTest(TestCase, UserTestMixin): class AccountViewsTest(TestCase, UserTestMixin):
def setUp(self): def setUp(self):
User.objects.create_user('test', None, 'test_pw') User.objects.create_user("test", None, "test_pw")
self.client.login(username='test', password='test_pw') self.client.login(username="test", password="test_pw")
def test_account(self): def test_account(self):
response = self.client.get('/account/') response = self.client.get("/account/")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_settings_form(self): def test_settings_form(self):
response = self.client.get('/account/settings') response = self.client.get("/account/settings")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def print_message(self, response): def print_message(self, response):
from django.contrib.messages import get_messages from django.contrib.messages import get_messages
messages = list(get_messages(response.wsgi_request)) messages = list(get_messages(response.wsgi_request))
for m in messages: for m in messages:
print(f"[message: {m.message!r} level={m.level} tags={m.tags!r}]") print(f"[message: {m.message!r} level={m.level} tags={m.tags!r}]")
def test_settings_post_email(self): def test_settings_post_email(self):
response = self.client.post('/account/settings', { response = self.client.post(
'action': 'email', "/account/settings",
'current_password': 'test_pw', {
'email': 'new_email@example.com'}) "action": "email",
"current_password": "test_pw",
"email": "new_email@example.com",
},
)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
user = User.objects.get(username='test') user = User.objects.get(username="test")
self.assertEqual(user.email, 'new_email@example.com') self.assertEqual(user.email, "new_email@example.com")
def test_settings_post_email_fail(self): def test_settings_post_email_fail(self):
response = self.client.post('/account/settings', { response = self.client.post(
'action': 'email', "/account/settings",
'current_password': 'not_test_pw', {
'email': 'new_email@example.com'}) "action": "email",
"current_password": "not_test_pw",
"email": "new_email@example.com",
},
)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
user = User.objects.get(username='test') user = User.objects.get(username="test")
self.assertNotEqual(user.email, 'new_email@example.com') self.assertNotEqual(user.email, "new_email@example.com")
def test_settings_post_pw(self): def test_settings_post_pw(self):
response = self.client.post('/account/settings', { response = self.client.post(
'action': 'password', "/account/settings",
'current_password': 'test_pw', {
'password': 'new_test_pw', 'password2': 'new_test_pw'}) "action": "password",
"current_password": "test_pw",
"password": "new_test_pw",
"password2": "new_test_pw",
},
)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
user = User.objects.get(username='test') user = User.objects.get(username="test")
self.assertTrue(user.check_password('new_test_pw')) self.assertTrue(user.check_password("new_test_pw"))
def test_settings_post_pw_fail(self): def test_settings_post_pw_fail(self):
response = self.client.post('/account/settings', { response = self.client.post(
'action': 'password', "/account/settings",
'current_password': 'oops', {
'password': 'new_test_pw', "action": "password",
'password2': 'new_test_pw'}) "current_password": "oops",
"password": "new_test_pw",
"password2": "new_test_pw",
},
)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
response = self.client.post('/account/settings', { response = self.client.post(
'action': 'password', "/account/settings",
'current_password': 'test_pw', {
'password': 'new_test_pw2', "action": "password",
'password2': 'new_test_pw_qsdfg'}) "current_password": "test_pw",
"password": "new_test_pw2",
"password2": "new_test_pw_qsdfg",
},
)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
user = User.objects.get(username='test') user = User.objects.get(username="test")
self.assertFalse(user.check_password('new_test_pw')) self.assertFalse(user.check_password("new_test_pw"))
self.assertFalse(user.check_password('new_test_pw2')) self.assertFalse(user.check_password("new_test_pw2"))
self.assertTrue(user.check_password('test_pw')) self.assertTrue(user.check_password("test_pw"))
class CACrtViewTest(TestCase): class CACrtViewTest(TestCase):
def test_ca_crt(self): def test_ca_crt(self):
with self.settings(OPENVPN_CA='test ca'): with self.settings(OPENVPN_CA="test ca"):
response = self.client.get('/ca.crt') response = self.client.get("/ca.crt")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], 'application/x-x509-ca-cert') self.assertEqual(response["Content-Type"], "application/x-x509-ca-cert")
self.assertEqual(response.content, b'test ca') self.assertEqual(response.content, b"test ca")
def email_text(body): def email_text(body):
return body.replace('\n', ' ') \ return body.replace("\n", " ").replace("\xa0", " ") # nbsp
.replace('\xa0', ' ') # nbsp
class ExpireNotifyTest(TestCase): class ExpireNotifyTest(TestCase):
@ -167,68 +195,68 @@ class ExpireNotifyTest(TestCase):
def test_notify_first(self): def test_notify_first(self):
out = StringIO() out = StringIO()
u = User.objects.create_user('test_username', 'test@example.com', 'testpw') u = User.objects.create_user("test_username", "test@example.com", "testpw")
u.vpnuser.add_paid_time(timedelta(days=2), 'initial') u.vpnuser.add_paid_time(timedelta(days=2), "initial")
u.vpnuser.save() u.vpnuser.save()
call_command('expire_notify', stdout=out) call_command("expire_notify", stdout=out)
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ['test@example.com']) self.assertEqual(mail.outbox[0].to, ["test@example.com"])
self.assertIn('expire in 1 day', email_text(mail.outbox[0].body)) self.assertIn("expire in 1 day", email_text(mail.outbox[0].body))
u = User.objects.get(username='test_username') u = User.objects.get(username="test_username")
self.assertAlmostEqual(u.vpnuser.last_expiry_notice, timezone.now(), self.assertAlmostEqual(
delta=timedelta(minutes=1)) u.vpnuser.last_expiry_notice, timezone.now(), delta=timedelta(minutes=1)
)
def test_notify_second(self): def test_notify_second(self):
out = StringIO() out = StringIO()
u = User.objects.create_user('test_username', 'test@example.com', 'testpw') u = User.objects.create_user("test_username", "test@example.com", "testpw")
u.vpnuser.last_expiry_notice = timezone.now() - timedelta(days=2) u.vpnuser.last_expiry_notice = timezone.now() - timedelta(days=2)
u.vpnuser.add_paid_time(timedelta(days=1), 'initial') u.vpnuser.add_paid_time(timedelta(days=1), "initial")
u.vpnuser.save() u.vpnuser.save()
call_command('expire_notify', stdout=out) call_command("expire_notify", stdout=out)
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ['test@example.com']) self.assertEqual(mail.outbox[0].to, ["test@example.com"])
self.assertIn('expire in 23 hours, 59 minutes', email_text(mail.outbox[0].body)) self.assertIn("expire in 23 hours, 59 minutes", email_text(mail.outbox[0].body))
u = User.objects.get(username='test_username') u = User.objects.get(username="test_username")
self.assertAlmostEqual(u.vpnuser.last_expiry_notice, timezone.now(), self.assertAlmostEqual(
delta=timedelta(minutes=1)) u.vpnuser.last_expiry_notice, timezone.now(), delta=timedelta(minutes=1)
)
def test_notify_subscription(self): def test_notify_subscription(self):
out = StringIO() out = StringIO()
u = User.objects.create_user('test_username', 'test@example.com', 'testpw') u = User.objects.create_user("test_username", "test@example.com", "testpw")
u.vpnuser.add_paid_time(timedelta(days=2), 'initial') u.vpnuser.add_paid_time(timedelta(days=2), "initial")
u.vpnuser.save() u.vpnuser.save()
s = Subscription(user=u, backend_id='paypal', status='active') s = Subscription(user=u, backend_id="paypal", status="active")
s.save() s.save()
call_command('expire_notify', stdout=out) call_command("expire_notify", stdout=out)
self.assertEqual(len(mail.outbox), 0) self.assertEqual(len(mail.outbox), 0)
u = User.objects.get(username='test_username') u = User.objects.get(username="test_username")
# FIXME: # FIXME:
# self.assertNotAlmostEqual(u.vpnuser.last_expiry_notice, timezone.now(), # self.assertNotAlmostEqual(u.vpnuser.last_expiry_notice, timezone.now(),
# delta=timedelta(minutes=1)) # delta=timedelta(minutes=1))
def test_notify_subscription_new(self): def test_notify_subscription_new(self):
out = StringIO() out = StringIO()
u = User.objects.create_user('test_username', 'test@example.com', 'testpw') u = User.objects.create_user("test_username", "test@example.com", "testpw")
u.vpnuser.add_paid_time(timedelta(days=2), 'initial') u.vpnuser.add_paid_time(timedelta(days=2), "initial")
u.vpnuser.last_expiry_notice = timezone.now() - timedelta(days=5) u.vpnuser.last_expiry_notice = timezone.now() - timedelta(days=5)
u.vpnuser.save() u.vpnuser.save()
s = Subscription(user=u, backend_id='paypal', status='new') s = Subscription(user=u, backend_id="paypal", status="new")
s.save() s.save()
call_command('expire_notify', stdout=out) call_command("expire_notify", stdout=out)
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
u = User.objects.get(username='test_username') u = User.objects.get(username="test_username")
# FIXME: # FIXME:
# self.assertNotAlmostEqual(u.vpnuser.last_expiry_notice, timezone.now(), # self.assertNotAlmostEqual(u.vpnuser.last_expiry_notice, timezone.now(),
# delta=timedelta(minutes=1)) # delta=timedelta(minutes=1))

@ -4,18 +4,17 @@ import django_lcore
from . import views from . import views
app_name = 'lambdainst' app_name = "lambdainst"
urlpatterns = [ urlpatterns = [
path('login', auth_views.LoginView.as_view(), name='login'), path("login", auth_views.LoginView.as_view(), name="login"),
path('discourse_login', views.discourse_login, name='discourse_login'), path("discourse_login", views.discourse_login, name="discourse_login"),
path('logout', views.logout, name='logout'), path("logout", views.logout, name="logout"),
path('signup', views.signup, name='signup'), path("signup", views.signup, name="signup"),
path("settings", views.settings, name="account_settings"),
path('settings', views.settings, name='account_settings'), path("config", views.config),
path('config', views.config), path("config_dl", django_lcore.views.openvpn_dl),
path('config_dl', django_lcore.views.openvpn_dl), path("wireguard", views.wireguard),
path('wireguard', views.wireguard), path("wireguard/new", views.wireguard_new, name="wireguard_new"),
path('wireguard/new', views.wireguard_new, name='wireguard_new'), path("", views.index, name="index"),
path('', views.index, name='index'),
] ]

@ -14,9 +14,12 @@ from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import Count from django.db.models import Count
from django.db import transaction from django.db import transaction
from django.http import (HttpResponse, from django.http import (
HttpResponseNotFound, HttpResponseRedirect, HttpResponse,
JsonResponse) HttpResponseNotFound,
HttpResponseRedirect,
JsonResponse,
)
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -33,16 +36,18 @@ from . import graphs
def get_locations(): def get_locations():
""" Pretty bad thing that returns get_locations() with translated stuff """Pretty bad thing that returns get_locations() with translated stuff
that depends on the request that depends on the request
""" """
countries_d = dict(countries) countries_d = dict(countries)
locations = django_lcore.get_clusters() locations = django_lcore.get_clusters()
for (_), loc in locations: for (_), loc in locations:
code = loc['country_code'].upper() code = loc["country_code"].upper()
loc['country_name'] = countries_d.get(code, code) loc["country_name"] = countries_d.get(code, code)
loc['load_percent'] = ceil((loc['usage'].get('net', 0) / (loc['bandwidth'] / 1e6)) * 100) loc["load_percent"] = ceil(
(loc["usage"].get("net", 0) / (loc["bandwidth"] / 1e6)) * 100
)
locations = list(sorted(locations, key=lambda l: l[0])) locations = list(sorted(locations, key=lambda l: l[0]))
return locations return locations
@ -56,58 +61,61 @@ def log_errors(request, form):
def ca_crt(request): def ca_crt(request):
return HttpResponse(content=project_settings.OPENVPN_CA, return HttpResponse(
content_type='application/x-x509-ca-cert') content=project_settings.OPENVPN_CA, content_type="application/x-x509-ca-cert"
)
def logout(request): def logout(request):
auth.logout(request) auth.logout(request)
return redirect('index') return redirect("index")
def signup(request): def signup(request):
if request.user.is_authenticated: if request.user.is_authenticated:
return redirect('account:index') return redirect("account:index")
if request.method != 'POST': if request.method != "POST":
form = SignupForm() form = SignupForm()
return render(request, 'ccvpn/signup.html', dict(form=form)) return render(request, "ccvpn/signup.html", dict(form=form))
form = SignupForm(request.POST) form = SignupForm(request.POST)
grr = request.POST.get('g-recaptcha-response', '') grr = request.POST.get("g-recaptcha-response", "")
if captcha_test(grr, request): if captcha_test(grr, request):
request.session['signup_captcha_pass'] = True request.session["signup_captcha_pass"] = True
elif not request.session.get('signup_captcha_pass'): elif not request.session.get("signup_captcha_pass"):
messages.error(request, _("Invalid captcha. Please try again")) messages.error(request, _("Invalid captcha. Please try again"))
return render(request, 'ccvpn/signup.html', dict(form=form)) return render(request, "ccvpn/signup.html", dict(form=form))
if not form.is_valid(): if not form.is_valid():
return render(request, 'ccvpn/signup.html', dict(form=form)) return render(request, "ccvpn/signup.html", dict(form=form))
user = User.objects.create_user(form.cleaned_data['username'], user = User.objects.create_user(
form.cleaned_data['email'], form.cleaned_data["username"],
form.cleaned_data['password']) form.cleaned_data["email"],
form.cleaned_data["password"],
)
user.save() user.save()
try: try:
user.vpnuser.referrer = User.objects.get(id=request.session.get('referrer')) user.vpnuser.referrer = User.objects.get(id=request.session.get("referrer"))
except User.DoesNotExist: except User.DoesNotExist:
pass pass
user.vpnuser.campaign = request.session.get('campaign') user.vpnuser.campaign = request.session.get("campaign")
user.vpnuser.add_paid_time(timedelta(days=7), 'trial') user.vpnuser.add_paid_time(timedelta(days=7), "trial")
user.vpnuser.save() user.vpnuser.save()
user.vpnuser.lcore_sync() user.vpnuser.lcore_sync()
user.backend = 'django.contrib.auth.backends.ModelBackend' user.backend = "django.contrib.auth.backends.ModelBackend"
auth.login(request, user) auth.login(request, user)
# invalidate that captcha # invalidate that captcha
request.session['signup_captcha_pass'] = False request.session["signup_captcha_pass"] = False
return redirect('account:index') return redirect("account:index")
@login_required @login_required
@ -118,70 +126,73 @@ def discourse_login(request):
if project_settings.DISCOURSE_SSO is not True: if project_settings.DISCOURSE_SSO is not True:
return HttpResponseNotFound() return HttpResponseNotFound()
payload = request.GET.get('sso', '') payload = request.GET.get("sso", "")
signature = request.GET.get('sig', '') signature = request.GET.get("sig", "")
expected_signature = hmac.new(sso_secret.encode('utf-8'), expected_signature = hmac.new(
payload.encode('utf-8'), sso_secret.encode("utf-8"), payload.encode("utf-8"), sha256
sha256).hexdigest() ).hexdigest()
if signature != expected_signature: if signature != expected_signature:
return HttpResponseNotFound() return HttpResponseNotFound()
if request.method == 'POST' and 'email' in request.POST: if request.method == "POST" and "email" in request.POST:
form = ReqEmailForm(request.POST) form = ReqEmailForm(request.POST)
if not form.is_valid(): if not form.is_valid():
return render(request, 'ccvpn/require_email.html', dict(form=form)) return render(request, "ccvpn/require_email.html", dict(form=form))
request.user.email = form.cleaned_data['email'] request.user.email = form.cleaned_data["email"]
request.user.save() request.user.save()
if not request.user.email: if not request.user.email:
form = ReqEmailForm() form = ReqEmailForm()
return render(request, 'ccvpn/require_email.html', dict(form=form)) return render(request, "ccvpn/require_email.html", dict(form=form))
try: try:
payload = base64.b64decode(payload).decode('utf-8') payload = base64.b64decode(payload).decode("utf-8")
payload_data = dict(parse_qsl(payload)) payload_data = dict(parse_qsl(payload))
except (TypeError, ValueError): except (TypeError, ValueError):
return HttpResponseNotFound() return HttpResponseNotFound()
payload_data.update({ payload_data.update(
'external_id': request.user.id, {
'username': request.user.username, "external_id": request.user.id,
'email': request.user.email, "username": request.user.username,
'require_activation': 'true', "email": request.user.email,
}) "require_activation": "true",
}
)
payload = urlencode(payload_data) payload = urlencode(payload_data)
payload = base64.b64encode(payload.encode('utf-8')) payload = base64.b64encode(payload.encode("utf-8"))
signature = hmac.new(sso_secret.encode('utf-8'), payload, sha256).hexdigest() signature = hmac.new(sso_secret.encode("utf-8"), payload, sha256).hexdigest()
redirect_query = urlencode(dict(sso=payload, sig=signature)) redirect_query = urlencode(dict(sso=payload, sig=signature))
redirect_path = '/session/sso_login?' + redirect_query redirect_path = "/session/sso_login?" + redirect_query
return HttpResponseRedirect(discourse_url + redirect_path) return HttpResponseRedirect(discourse_url + redirect_path)
@login_required @login_required
def index(request): def index(request):
ref_url = project_settings.ROOT_URL + '?ref=' + str(request.user.id) ref_url = project_settings.ROOT_URL + "?ref=" + str(request.user.id)
twitter_url = 'https://twitter.com/intent/tweet?' twitter_url = "https://twitter.com/intent/tweet?"
twitter_args = { twitter_args = {
'text': _("Awesome VPN! 3€ per month, with a free 7 days trial!"), "text": _("Awesome VPN! 3€ per month, with a free 7 days trial!"),
'via': 'CCrypto_VPN', "via": "CCrypto_VPN",
'url': ref_url, "url": ref_url,
'related': 'CCrypto_VPN,CCrypto_org' "related": "CCrypto_VPN,CCrypto_org",
} }
class price_fn: class price_fn:
""" Clever hack to get the price in templates with {{price.3}} with """Clever hack to get the price in templates with {{price.3}} with
3 an arbitrary number of months 3 an arbitrary number of months
""" """
def __getitem__(self, months): def __getitem__(self, months):
n = int(months) * get_price_float() n = int(months) * get_price_float()
c = project_settings.PAYMENTS_CURRENCY[1] c = project_settings.PAYMENTS_CURRENCY[1]
return '%.2f %s' % (n, c) return "%.2f %s" % (n, c)
context = dict( context = dict(
title=_("Account"), title=_("Account"),
@ -189,15 +200,16 @@ def index(request):
twitter_link=twitter_url + urlencode(twitter_args), twitter_link=twitter_url + urlencode(twitter_args),
subscription=request.user.vpnuser.get_subscription(), subscription=request.user.vpnuser.get_subscription(),
backends=sorted(ACTIVE_BACKENDS.values(), key=lambda x: x.backend_display_name), backends=sorted(ACTIVE_BACKENDS.values(), key=lambda x: x.backend_display_name),
subscr_backends=sorted((b for b in ACTIVE_BACKENDS.values() subscr_backends=sorted(
if b.backend_has_recurring), (b for b in ACTIVE_BACKENDS.values() if b.backend_has_recurring),
key=lambda x: x.backend_id), key=lambda x: x.backend_id,
default_backend='paypal', ),
default_backend="paypal",
hcaptcha_site_key=project_settings.HCAPTCHA_SITE_KEY, hcaptcha_site_key=project_settings.HCAPTCHA_SITE_KEY,
price=price_fn(), price=price_fn(),
user_motd=site_config.MOTD_USER, user_motd=site_config.MOTD_USER,
) )
return render(request, 'lambdainst/account.html', context) return render(request, "lambdainst/account.html", context)
def captcha_test(grr, request): def captcha_test(grr, request):
@ -206,18 +218,17 @@ def captcha_test(grr, request):
if not project_settings.HCAPTCHA_SITE_KEY: if not project_settings.HCAPTCHA_SITE_KEY:
return True return True
if api_url == 'TEST' and grr == 'TEST-TOKEN': if api_url == "TEST" and grr == "TEST-TOKEN":
# FIXME: i'm sorry. # FIXME: i'm sorry.
return True return True
data = dict(secret=project_settings.HCAPTCHA_SECRET_KEY, data = dict(secret=project_settings.HCAPTCHA_SECRET_KEY, response=grr)
response=grr)
try: try:
r = requests.post(api_url, data=data) r = requests.post(api_url, data=data)
r.raise_for_status() r.raise_for_status()
d = r.json() d = r.json()
return d.get('success') return d.get("success")
except (requests.ConnectionError, requests.HTTPError, ValueError): except (requests.ConnectionError, requests.HTTPError, ValueError):
return False return False
@ -233,13 +244,29 @@ def make_export_zip(user, name):
gw_cache = {} gw_cache = {}
def process_wg_peer(item): def process_wg_peer(item):
keys = {"gateway_port", "id", "local_ipv4", "local_ipv6", "name", "object", keys = {
"private_key", "public_key"} "gateway_port",
"id",
"local_ipv4",
"local_ipv6",
"name",
"object",
"private_key",
"public_key",
}
return {k: v for (k, v) in item.items() if k in keys} return {k: v for (k, v) in item.items() if k in keys}
def process_ovpn_sess(item): def process_ovpn_sess(item):
keys = {"connect_date", "disconnect_date", "remote", "object", "protocol", "id", keys = {
"stats", "tunnel"} "connect_date",
"disconnect_date",
"remote",
"object",
"protocol",
"id",
"stats",
"tunnel",
}
def convert(v): def convert(v):
if isinstance(v, datetime): if isinstance(v, datetime):
@ -277,41 +304,50 @@ def make_export_zip(user, name):
return obj return obj
with z.open(name + "/account.json", "w") as jf: with z.open(name + "/account.json", "w") as jf:
jf.write(json.dumps({ jf.write(
json.dumps(
{
"username": user.username, "username": user.username,
"email": user.email, "email": user.email,
"date_joined": user.date_joined.isoformat(), "date_joined": user.date_joined.isoformat(),
"expiration": user.vpnuser.expiration.isoformat() if user.vpnuser.expiration else None, "expiration": user.vpnuser.expiration.isoformat()
}, indent=2).encode('ascii')) if user.vpnuser.expiration
else None,
},
indent=2,
).encode("ascii")
)
with z.open(name + "/wireguard_peers.json", "w") as jf: with z.open(name + "/wireguard_peers.json", "w") as jf:
try: try:
keys = list(map(process_wg_peer, django_lcore.api.get_wg_peers(user.username))) keys = list(
map(process_wg_peer, django_lcore.api.get_wg_peers(user.username))
)
except lcoreapi.APINotFoundError: except lcoreapi.APINotFoundError:
keys = [] keys = []
jf.write(json.dumps(keys, indent=2).encode('ascii')) jf.write(json.dumps(keys, indent=2).encode("ascii"))
with z.open(name + "/openvpn_logs.json", "w") as jf: with z.open(name + "/openvpn_logs.json", "w") as jf:
base = django_lcore.api.info['current_instance'] base = django_lcore.api.info["current_instance"]
next_page = '/users/' + user.username + '/sessions/' next_page = "/users/" + user.username + "/sessions/"
try: try:
items = django_lcore.api.get(base + next_page).list_iter() items = django_lcore.api.get(base + next_page).list_iter()
except lcoreapi.APINotFoundError: except lcoreapi.APINotFoundError:
items = [] items = []
items = list(map(process_ovpn_sess, items)) items = list(map(process_ovpn_sess, items))
jf.write(json.dumps(items, indent=2).encode('ascii')) jf.write(json.dumps(items, indent=2).encode("ascii"))
with z.open(name + "/payments.json", "w") as jf: with z.open(name + "/payments.json", "w") as jf:
items = user.payment_set.all() items = user.payment_set.all()
items = list(map(process_payments, items)) items = list(map(process_payments, items))
jf.write(json.dumps(items, indent=2).encode('ascii')) jf.write(json.dumps(items, indent=2).encode("ascii"))
z.close() z.close()
return f.getvalue() return f.getvalue()
def deactivate_user(user): def deactivate_user(user):
""" clear most information from a user, keeping the username and id """ """clear most information from a user, keeping the username and id"""
user.vpnuser.clear_fields() user.vpnuser.clear_fields()
user.vpnuser.save() user.vpnuser.save()
@ -330,33 +366,37 @@ def deactivate_user(user):
def settings(request): def settings(request):
can_delete = request.user.vpnuser.get_subscription() is None can_delete = request.user.vpnuser.get_subscription() is None
if request.method != 'POST': if request.method != "POST":
return render(request, 'lambdainst/settings.html', dict( return render(
request,
"lambdainst/settings.html",
dict(
can_delete=can_delete, can_delete=can_delete,
)) ),
)
action = request.POST.get('action') action = request.POST.get("action")
current_pw = request.POST.get('current_password') current_pw = request.POST.get("current_password")
if not request.user.check_password(current_pw): if not request.user.check_password(current_pw):
messages.error(request, _("Invalid password")) messages.error(request, _("Invalid password"))
return redirect('lambdainst:account_settings') return redirect("lambdainst:account_settings")
if action == 'email': if action == "email":
email = request.POST.get('email') email = request.POST.get("email")
if email: if email:
request.user.email = email request.user.email = email
messages.success(request, _("OK! Email address changed.")) messages.success(request, _("OK! Email address changed."))
else: else:
request.user.email = '' request.user.email = ""
messages.success(request, _("OK! Email address unset.")) messages.success(request, _("OK! Email address unset."))
request.user.save() request.user.save()
return redirect('lambdainst:account_settings') return redirect("lambdainst:account_settings")
elif action == 'password': elif action == "password":
pw = request.POST.get('password') pw = request.POST.get("password")
pw2 = request.POST.get('password2') pw2 = request.POST.get("password2")
if pw != pw2 or not pw: if pw != pw2 or not pw:
messages.error(request, _("Password and confirmation do not match")) messages.error(request, _("Password and confirmation do not match"))
else: else:
@ -364,95 +404,120 @@ def settings(request):
messages.success(request, _("OK! Password changed.")) messages.success(request, _("OK! Password changed."))
request.user.save() request.user.save()
django_lcore.sync_user(request.user.vpnuser) django_lcore.sync_user(request.user.vpnuser)
return redirect('lambdainst:account_settings') return redirect("lambdainst:account_settings")
elif action == 'export': elif action == "export":
timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
data = make_export_zip(request.user, timestamp) data = make_export_zip(request.user, timestamp)
r = HttpResponse(content=data, content_type="application/zip") r = HttpResponse(content=data, content_type="application/zip")
r["Content-Disposition"] = 'attachment; filename="ccvpn-export-{}-{}.zip"'.format(request.user.username, timestamp) r[
"Content-Disposition"
] = 'attachment; filename="ccvpn-export-{}-{}.zip"'.format(
request.user.username, timestamp
)
return r return r
elif action == 'delete' and can_delete: elif action == "delete" and can_delete:
with transaction.atomic(): with transaction.atomic():
deactivate_user(request.user) deactivate_user(request.user)
logout(request) logout(request)
messages.success(request, _("OK! Your account has been deactivated.")) messages.success(request, _("OK! Your account has been deactivated."))
return redirect('/') return redirect("/")
return render(request, 'lambdainst/settings.html', dict( return render(
request,
"lambdainst/settings.html",
dict(
title=_("Settings"), title=_("Settings"),
user=request.user, user=request.user,
can_delete=can_delete, can_delete=can_delete,
)) ),
)
@login_required @login_required
def config(request): def config(request):
return render(request, 'lambdainst/config.html', dict( return render(
request,
"lambdainst/config.html",
dict(
title=_("Config"), title=_("Config"),
config_os=django_lcore.openvpn.CONFIG_OS, config_os=django_lcore.openvpn.CONFIG_OS,
config_countries=(c for _, c in get_locations()), config_countries=(c for _, c in get_locations()),
config_protocols=django_lcore.openvpn.PROTOCOLS, config_protocols=django_lcore.openvpn.PROTOCOLS,
)) ),
)
def api_locations(request): def api_locations(request):
def format_loc(cc, l): def format_loc(cc, l):
msg = "" msg = ""
tags = l.get('tags', {}) tags = l.get("tags", {})
message = tags.get('message') message = tags.get("message")
if message: if message:
msg = " [%s]" % message msg = " [%s]" % message
return { return {
'country_name': l['country_name'] + msg, "country_name": l["country_name"] + msg,
'country_code': cc, "country_code": cc,
'hostname': l['hostname'], "hostname": l["hostname"],
'bandwidth': l['bandwidth'], "bandwidth": l["bandwidth"],
'servers': l['servers'], "servers": l["servers"],
} }
return JsonResponse(dict(locations=[format_loc(cc, l) for cc, l in get_locations()]))
return JsonResponse(
dict(locations=[format_loc(cc, l) for cc, l in get_locations()])
)
def status(request): def status(request):
locations = get_locations() locations = get_locations()
ctx = { ctx = {
'title': _("Servers"), "title": _("Servers"),
'locations': locations, "locations": locations,
} }
return render(request, 'lambdainst/status.html', ctx) return render(request, "lambdainst/status.html", ctx)
@user_passes_test(lambda user: user.is_staff) @user_passes_test(lambda user: user.is_staff)
def admin_status(request): def admin_status(request):
graph_name = request.GET.get('graph_name') graph_name = request.GET.get("graph_name")
graph_period = request.GET.get('period') graph_period = request.GET.get("period")
if graph_period not in ('y', 'm'): if graph_period not in ("y", "m"):
graph_period = 'm' graph_period = "m"
if graph_name: if graph_name:
if graph_name == 'users': if graph_name == "users":
content = graphs.users_graph(graph_period) content = graphs.users_graph(graph_period)
elif graph_name == 'payments_paid': elif graph_name == "payments_paid":
content = graphs.payments_paid_graph(graph_period) content = graphs.payments_paid_graph(graph_period)
elif graph_name == 'payments_success': elif graph_name == "payments_success":
content = graphs.payments_success_graph(graph_period) content = graphs.payments_success_graph(graph_period)
else: else:
return HttpResponseNotFound() return HttpResponseNotFound()
return HttpResponse(content=content, content_type='image/svg+xml') return HttpResponse(content=content, content_type="image/svg+xml")
payment_status = ((b, b.get_info()) for b in ACTIVE_BACKENDS.values()) payment_status = ((b, b.get_info()) for b in ACTIVE_BACKENDS.values())
payment_status = ((b, i) for (b, i) in payment_status if i) payment_status = ((b, i) for (b, i) in payment_status if i)
lcore_keys = {'core_name', 'core_now', 'core_version', 'current_instance', 'key_public'} lcore_keys = {
"core_name",
"core_now",
"core_version",
"current_instance",
"key_public",
}
ctx = { ctx = {
'api_status': {k: str(v) for k, v in django_lcore.api.info.items() if k in lcore_keys}, "api_status": {
'payment_backends': sorted(ACTIVE_BACKENDS.values(), key=lambda x: x.backend_id), k: str(v) for k, v in django_lcore.api.info.items() if k in lcore_keys
'payment_status': payment_status, },
"payment_backends": sorted(
ACTIVE_BACKENDS.values(), key=lambda x: x.backend_id
),
"payment_status": payment_status,
} }
ctx.update(site.each_context(request)) ctx.update(site.each_context(request))
return render(request, 'lambdainst/admin_status.html', ctx) return render(request, "lambdainst/admin_status.html", ctx)
@user_passes_test(lambda user: user.is_staff) @user_passes_test(lambda user: user.is_staff)
@ -460,36 +525,40 @@ def admin_ref(request):
last_week = datetime.now() - timedelta(days=7) last_week = datetime.now() - timedelta(days=7)
last_month = datetime.now() - timedelta(days=30) last_month = datetime.now() - timedelta(days=30)
top_ref = User.objects.annotate(n_ref=Count('referrals')).order_by('-n_ref')[:10] top_ref = User.objects.annotate(n_ref=Count("referrals")).order_by("-n_ref")[:10]
top_ref_week = User.objects.filter(referrals__user__date_joined__gt=last_week) \ top_ref_week = (
.annotate(n_ref=Count('referrals')) \ User.objects.filter(referrals__user__date_joined__gt=last_week)
.order_by('-n_ref')[:10] .annotate(n_ref=Count("referrals"))
top_ref_month = User.objects.filter(referrals__user__date_joined__gt=last_month) \ .order_by("-n_ref")[:10]
.annotate(n_ref=Count('referrals')) \ )
.order_by('-n_ref')[:10] top_ref_month = (
User.objects.filter(referrals__user__date_joined__gt=last_month)
.annotate(n_ref=Count("referrals"))
.order_by("-n_ref")[:10]
)
ctx = { ctx = {
'top_ref': top_ref, "top_ref": top_ref,
'top_ref_week': top_ref_week, "top_ref_week": top_ref_week,
'top_ref_month': top_ref_month, "top_ref_month": top_ref_month,
} }
ctx.update(site.each_context(request)) ctx.update(site.each_context(request))
return render(request, 'lambdainst/admin_ref.html', ctx) return render(request, "lambdainst/admin_ref.html", ctx)
@login_required @login_required
def wireguard(request): def wireguard(request):
api = django_lcore.api api = django_lcore.api
if request.method == 'POST': if request.method == "POST":
action = request.POST.get('action') action = request.POST.get("action")
if action == 'delete_key': if action == "delete_key":
key = api.get_wg_peer(request.user.username, request.POST.get('peer_id')) key = api.get_wg_peer(request.user.username, request.POST.get("peer_id"))
if key: if key:
key.delete() key.delete()
elif action == 'set_name': elif action == "set_name":
key = api.get_wg_peer(request.user.username, request.POST.get('peer_id')) key = api.get_wg_peer(request.user.username, request.POST.get("peer_id"))
if key: if key:
name = request.POST.get('name') name = request.POST.get("name")
if name: if name:
name = name[:21] name = name[:21]
key.rename(name) key.rename(name)
@ -503,19 +572,19 @@ def wireguard(request):
context = dict( context = dict(
can_create_key=len(keys) < int(site_config.WIREGUARD_MAX_PEERS), can_create_key=len(keys) < int(site_config.WIREGUARD_MAX_PEERS),
menu_item='wireguard', menu_item="wireguard",
enabled=request.user.vpnuser.is_paid, enabled=request.user.vpnuser.is_paid,
config_countries=[(k, v) for (k, v) in django_lcore.get_clusters()], config_countries=[(k, v) for (k, v) in django_lcore.get_clusters()],
keys=keys, keys=keys,
locations=get_locations(), locations=get_locations(),
) )
return render(request, 'lambdainst/wireguard.html', context) return render(request, "lambdainst/wireguard.html", context)
@login_required @login_required
def wireguard_new(request): def wireguard_new(request):
if not request.user.vpnuser.is_paid: if not request.user.vpnuser.is_paid:
return redirect('account:index') return redirect("account:index")
try: try:
keys = django_lcore.api.get_wg_peers(request.user.username) keys = django_lcore.api.get_wg_peers(request.user.username)
@ -523,25 +592,24 @@ def wireguard_new(request):
django_lcore.sync_user(request.user.vpnuser) django_lcore.sync_user(request.user.vpnuser)
keys = [] keys = []
if len(keys) >= int(site_config.WIREGUARD_MAX_PEERS): if len(keys) >= int(site_config.WIREGUARD_MAX_PEERS):
return redirect('/account/wireguard') return redirect("/account/wireguard")
api = django_lcore.api api = django_lcore.api
if request.method == 'POST': if request.method == "POST":
action = request.POST.get('action') action = request.POST.get("action")
form = WgPeerForm(request.POST) form = WgPeerForm(request.POST)
if action == 'add_key': if action == "add_key":
if form.is_valid(): if form.is_valid():
api.create_wg_peer( api.create_wg_peer(
request.user.username, request.user.username,
public_key=form.cleaned_data['public_key'], public_key=form.cleaned_data["public_key"],
name=form.cleaned_data['name'], name=form.cleaned_data["name"],
) )
else: else:
log_errors(request, form) log_errors(request, form)
return redirect('/account/wireguard') return redirect("/account/wireguard")
context = dict( context = dict(
menu_item='wireguard', menu_item="wireguard",
locations=get_locations(), locations=get_locations(),
) )
return render(request, 'lambdainst/wireguard_new.html', context) return render(request, "lambdainst/wireguard_new.html", context)

83
poetry.lock generated

@ -71,25 +71,27 @@ python-versions = "*"
[[package]] [[package]]
name = "black" name = "black"
version = "20.8b1" version = "21.7b0"
description = "The uncompromising code formatter." description = "The uncompromising code formatter."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6.2"
[package.dependencies] [package.dependencies]
appdirs = "*" appdirs = "*"
click = ">=7.1.2" click = ">=7.1.2"
mypy-extensions = ">=0.4.3" mypy-extensions = ">=0.4.3"
pathspec = ">=0.6,<1" pathspec = ">=0.8.1,<1"
regex = ">=2020.1.8" regex = ">=2020.1.8"
toml = ">=0.10.1" tomli = ">=0.2.6,<2.0.0"
typed-ast = ">=1.4.0" typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""}
typing-extensions = ">=3.7.4" typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
[package.extras] [package.extras]
colorama = ["colorama (>=0.4.3)"] colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"]
python2 = ["typed-ast (>=1.4.2)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]] [[package]]
name = "cached-property" name = "cached-property"
@ -408,18 +410,18 @@ jsmin = "*"
[[package]] [[package]]
name = "flower" name = "flower"
version = "0.9.5" version = "1.0.0"
description = "Celery Flower" description = "Celery Flower"
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[package.dependencies] [package.dependencies]
celery = {version = ">=4.3.0", markers = "python_version >= \"3.7\""} celery = ">=5.0.5"
humanize = "*" humanize = "*"
prometheus-client = "0.8.0" prometheus-client = ">=0.8.0"
pytz = "*" pytz = "*"
tornado = {version = ">=5.0.0,<7.0.0", markers = "python_version >= \"3.5.2\""} tornado = ">=5.0.0,<7.0.0"
[[package]] [[package]]
name = "humanize" name = "humanize"
@ -499,16 +501,17 @@ python-versions = "*"
[[package]] [[package]]
name = "isort" name = "isort"
version = "5.8.0" version = "5.9.2"
description = "A Python utility / library to sort Python imports." description = "A Python utility / library to sort Python imports."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6,<4.0" python-versions = ">=3.6.1,<4.0"
[package.extras] [package.extras]
pipfile_deprecated_finder = ["pipreqs", "requirementslib"] pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
requirements_deprecated_finder = ["pipreqs", "pip-api"] requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"] colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
[[package]] [[package]]
name = "jedi" name = "jedi"
@ -682,22 +685,22 @@ python-versions = "*"
[[package]] [[package]]
name = "prometheus-client" name = "prometheus-client"
version = "0.8.0" version = "0.11.0"
description = "Python client for the Prometheus monitoring system." description = "Python client for the Prometheus monitoring system."
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.extras] [package.extras]
twisted = ["twisted"] twisted = ["twisted"]
[[package]] [[package]]
name = "prompt-toolkit" name = "prompt-toolkit"
version = "3.0.3" version = "3.0.19"
description = "Library for building powerful interactive command lines in Python" description = "Library for building powerful interactive command lines in Python"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6.1"
[package.dependencies] [package.dependencies]
wcwidth = "*" wcwidth = "*"
@ -782,14 +785,6 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
pylint = ">=1.7" pylint = ">=1.7"
[[package]]
name = "python-bitcoinlib"
version = "0.11.0"
description = "The Swiss Army Knife of the Bitcoin protocol."
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "python-crontab" name = "python-crontab"
version = "2.5.1" version = "2.5.1"
@ -930,6 +925,14 @@ category = "dev"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "tomli"
version = "1.1.0"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]] [[package]]
name = "tornado" name = "tornado"
version = "6.1" version = "6.1"
@ -1020,7 +1023,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
content-hash = "80258fdb7e8f9b695a3a5ad92efb52c47433243bccc905d701d1e377f38877d9" content-hash = "5e7e869eb1d76bb7621e1653d293566a4ad761932fcc33e74c88602554c6d7f4"
[metadata.files] [metadata.files]
amqp = [ amqp = [
@ -1052,8 +1055,8 @@ billiard = [
{file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"},
] ]
black = [ black = [
{file = "black-20.8b1-py3-none-any.whl", hash = "sha256:70b62ef1527c950db59062cda342ea224d772abdf6adc58b86a45421bab20a6b"}, {file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"},
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, {file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"},
] ]
cached-property = [ cached-property = [
{file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"},
@ -1137,8 +1140,8 @@ django-tinymce4-lite = [
{file = "django_tinymce4_lite-1.8.0-py3-none-any.whl", hash = "sha256:2d53510ddb5fe20f25e525d4eaf7c8f8a567b85c9cc29f8ab2964419d9352d88"}, {file = "django_tinymce4_lite-1.8.0-py3-none-any.whl", hash = "sha256:2d53510ddb5fe20f25e525d4eaf7c8f8a567b85c9cc29f8ab2964419d9352d88"},
] ]
flower = [ flower = [
{file = "flower-0.9.5-py2.py3-none-any.whl", hash = "sha256:71be02bff7b2f56b0a07bd947fb3c748acba7f44f80ae88125d8954ce1a89697"}, {file = "flower-1.0.0-py2.py3-none-any.whl", hash = "sha256:a4fcf959881135303e98a74cc7533298b7dfeb48abcd1d90c5bd52cb789430a8"},
{file = "flower-0.9.5.tar.gz", hash = "sha256:56916d1d2892e25453d6023437427fc04706a1308e0bd4822321da34e1643f9c"}, {file = "flower-1.0.0.tar.gz", hash = "sha256:2e17c4fb55c569508f3bfee7fe41f44b8362d30dbdf77b604a9d9f4740fe8cbd"},
] ]
humanize = [ humanize = [
{file = "humanize-3.10.0-py3-none-any.whl", hash = "sha256:aab7625d62dd5e0a054c8413a47d1fa257f3bdd8e9a2442c2fe36061bdd1d9bf"}, {file = "humanize-3.10.0-py3-none-any.whl", hash = "sha256:aab7625d62dd5e0a054c8413a47d1fa257f3bdd8e9a2442c2fe36061bdd1d9bf"},
@ -1161,8 +1164,8 @@ ipython-genutils = [
{file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
] ]
isort = [ isort = [
{file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, {file = "isort-5.9.2-py3-none-any.whl", hash = "sha256:eed17b53c3e7912425579853d078a0832820f023191561fcee9d7cae424e0813"},
{file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, {file = "isort-5.9.2.tar.gz", hash = "sha256:f65ce5bd4cbc6abdfbe29afc2f0245538ab358c14590912df638033f157d555e"},
] ]
jedi = [ jedi = [
{file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"},
@ -1237,12 +1240,12 @@ pickleshare = [
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
] ]
prometheus-client = [ prometheus-client = [
{file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"}, {file = "prometheus_client-0.11.0-py2.py3-none-any.whl", hash = "sha256:b014bc76815eb1399da8ce5fc84b7717a3e63652b0c0f8804092c9363acab1b2"},
{file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, {file = "prometheus_client-0.11.0.tar.gz", hash = "sha256:3a8baade6cb80bcfe43297e33e7623f3118d660d41387593758e2fb1ea173a86"},
] ]
prompt-toolkit = [ prompt-toolkit = [
{file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, {file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"},
{file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, {file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"},
] ]
psycopg2-binary = [ psycopg2-binary = [
{file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"}, {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
@ -1299,10 +1302,6 @@ pylint-plugin-utils = [
{file = "pylint-plugin-utils-0.6.tar.gz", hash = "sha256:57625dcca20140f43731311cd8fd879318bf45a8b0fd17020717a8781714a25a"}, {file = "pylint-plugin-utils-0.6.tar.gz", hash = "sha256:57625dcca20140f43731311cd8fd879318bf45a8b0fd17020717a8781714a25a"},
{file = "pylint_plugin_utils-0.6-py3-none-any.whl", hash = "sha256:2f30510e1c46edf268d3a195b2849bd98a1b9433229bb2ba63b8d776e1fc4d0a"}, {file = "pylint_plugin_utils-0.6-py3-none-any.whl", hash = "sha256:2f30510e1c46edf268d3a195b2849bd98a1b9433229bb2ba63b8d776e1fc4d0a"},
] ]
python-bitcoinlib = [
{file = "python-bitcoinlib-0.11.0.tar.gz", hash = "sha256:3daafd63cb755f6e2067b7c9c514053856034c9f9363c80c37007744d54a2e06"},
{file = "python_bitcoinlib-0.11.0-py3-none-any.whl", hash = "sha256:6e7982734637135599e2136d3c88d622f147e3b29201636665f799365784cd9e"},
]
python-crontab = [ python-crontab = [
{file = "python-crontab-2.5.1.tar.gz", hash = "sha256:4bbe7e720753a132ca4ca9d4094915f40e9d9dc8a807a4564007651018ce8c31"}, {file = "python-crontab-2.5.1.tar.gz", hash = "sha256:4bbe7e720753a132ca4ca9d4094915f40e9d9dc8a807a4564007651018ce8c31"},
] ]
@ -1420,6 +1419,10 @@ toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
] ]
tomli = [
{file = "tomli-1.1.0-py3-none-any.whl", hash = "sha256:f4a182048010e89cbec0ae4686b21f550a7f2903f665e34a6de58ec15424f919"},
{file = "tomli-1.1.0.tar.gz", hash = "sha256:33d7984738f8bb699c9b0a816eb646a8178a69eaa792d258486776a5d21b8ca5"},
]
tornado = [ tornado = [
{file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"},
{file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"},

@ -13,7 +13,6 @@ markdown = "^3.1"
requests = "^2.21" requests = "^2.21"
pygal = "^2.4" pygal = "^2.4"
pytz = "^2021.1" pytz = "^2021.1"
python-bitcoinlib = "^0.11"
stripe = "^2.24" stripe = "^2.24"
django-constance = {version = "^2.7",extras = ["database"]} django-constance = {version = "^2.7",extras = ["database"]}
lcoreapi = {git = "https://git.packetimpact.net/lvpn/lcoreapi.git", tag = "v1.1.1"} lcoreapi = {git = "https://git.packetimpact.net/lvpn/lcoreapi.git", tag = "v1.1.1"}
@ -28,13 +27,13 @@ django-pipayments = {git = "git@git.packetimpact.net:lvpn/django-pipayments.git"
celery = "^5" celery = "^5"
django-celery-beat = "^2.0.0" django-celery-beat = "^2.0.0"
redis = "^3.5.3" redis = "^3.5.3"
flower = "^0.9.5" flower = "^1"
django-extensions = "^3.1.3" django-extensions = "^3.1.3"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pylint = "^2.3" pylint = "^2.3"
pylint-django = "^2.0.14" pylint-django = "^2.0.14"
black = {version = "^20.8b1", allow-prereleases = true} black = {version = "^21.7b0", allow-prereleases = true}
selenium = "^3.141.0" selenium = "^3.141.0"
ipython = "^7.25.0" ipython = "^7.25.0"

Loading…
Cancel
Save