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

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

@ -8,8 +8,8 @@ from payments.models import Payment
PERIOD_VERBOSE_NAME = {
'y': "per month",
'm': "per day",
"y": "per month",
"m": "per day",
}
@ -18,9 +18,23 @@ def monthdelta(date, delta):
y = date.year + (date.month + delta - 1) // 12
if not m:
m = 12
d = min(date.day, [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])
d = min(
date.day,
[
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)
@ -38,57 +52,64 @@ def last_months(n=12):
def time_filter_future(period, m, df):
def _filter(o):
if period == 'm':
if period == "m":
return df(o).date() <= m
if period == 'y':
if period == "y":
return df(o).date().replace(day=1) <= m
return _filter
def time_filter_between(period, m, df):
def _filter(o):
if period == 'm':
return df(o).year == m.year and df(o).month == m.month and df(o).day == m.day
if period == "m":
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))
if period == 'y':
if period == "y":
return df(o).year == m.year and df(o).month == m.month
return (df(o).date().replace(day=1) <= m and
df(o).date().replace(day=1) > (m - timedelta(days=30)))
return df(o).date().replace(day=1) <= m and df(o).date().replace(day=1) > (
m - timedelta(days=30)
)
return _filter
def users_graph(period):
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 = []
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()
for m in gen:
filter_ = time_filter_future(period, m, lambda o: o.date_joined)
users_filtered = filter(filter_, users)
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()
def payments_paid_graph(period):
chart = pygal.StackedBar(x_label_rotation=75, show_legend=True)
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:
chart.x_labels.append('%02d/%02d' % (m.month, m.day))
chart.x_labels.append("%02d/%02d" % (m.month, m.day))
values = dict()
for backend_id, backend in BACKENDS.items():
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:
filter_ = time_filter_between(period, m, lambda o: o.created)
@ -103,17 +124,19 @@ def payments_paid_graph(period):
def payments_success_graph(period):
chart = pygal.StackedBar(x_label_rotation=75, show_legend=True)
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:
chart.x_labels.append('%02d/%02d' % (m.month, m.day))
chart.x_labels.append("%02d/%02d" % (m.month, m.day))
values = dict()
for backend_id, backend in BACKENDS.items():
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:
filter_ = time_filter_between(period, m, lambda o: o.created)
@ -123,4 +146,3 @@ def payments_success_graph(period):
chart.add(backend_id, values)
return chart.render()

@ -8,10 +8,10 @@ from .models import User
class ReferrerMiddleware(MiddlewareMixin):
def process_request(self, request):
if 'ref' in request.GET:
id = request.GET['ref']
elif 'referrer' in request.COOKIES:
id = request.COOKIES['referrer']
if "ref" in request.GET:
id = request.GET["ref"]
elif "referrer" in request.COOKIES:
id = request.COOKIES["referrer"]
else:
return
@ -25,33 +25,36 @@ class ReferrerMiddleware(MiddlewareMixin):
except User.DoesNotExist:
return
request.session['referrer'] = u.id
request.session["referrer"] = u.id
def process_response(self, request, response):
id = request.session.get('referrer')
id = request.session.get("referrer")
if not id:
return response
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")
response.set_cookie('referrer', id,
max_age=max_age,
expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None)
response.set_cookie(
"referrer",
id,
max_age=max_age,
expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None,
)
return response
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):
for f in self.GET_FIELDS:
if f in request.GET and request.GET[f]:
return request.GET[f]
if 'campaign' in request.COOKIES:
return request.COOKIES['campaign']
if "campaign" in request.COOKIES:
return request.COOKIES["campaign"]
return None
def process_request(self, request):
@ -61,22 +64,25 @@ class CampaignMiddleware(MiddlewareMixin):
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
request.session['campaign'] = name
request.session["campaign"] = name
def process_response(self, request, response):
name = request.session.get('campaign')
name = request.session.get("campaign")
if not name:
return response
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")
response.set_cookie('campaign', name,
max_age=max_age,
expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None)
response.set_cookie(
"campaign",
name,
max_age=max_age,
expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None,
)
return response

@ -2,7 +2,11 @@ import random
from django.db import models, IntegrityError
from django.contrib.auth.models import User
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
@ -11,7 +15,7 @@ prng = random.SystemRandom()
def random_gift_code():
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):
@ -33,8 +37,9 @@ class VPNUser(models.Model, BaseSubUser, LcoreUserProfileMethods):
last_vpn_auth = 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,
related_name='referrals')
referrer = models.ForeignKey(
User, blank=True, null=True, on_delete=models.SET_NULL, related_name="referrals"
)
referrer_used = models.BooleanField(default=False)
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):
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_used = True
self.save()
def lcore_sync(self):
if VPN_AUTH_STORAGE == 'inst':
if VPN_AUTH_STORAGE == "inst":
return
from lambdainst.tasks import push_user
push_user.delay(user_id=self.user.id)
def notify_payment(self, payment):
@ -72,12 +82,13 @@ class VPNUser(models.Model, BaseSubUser, LcoreUserProfileMethods):
def __str__(self):
return self.user.username
setup_sync_hooks(User, VPNUser)
#from django.db.models.signals import post_save
#from django.dispatch import receiver
#@receiver(post_save, sender=User)
#def create_vpnuser(sender, instance, created, **kwargs):
# from django.db.models.signals import post_save
# from django.dispatch import receiver
# @receiver(post_save, sender=User)
# def create_vpnuser(sender, instance, created, **kwargs):
# if created:
# try:
# VPNUser.objects.create(user=instance)

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

@ -69,7 +69,7 @@ def notify_vpn_auth_fails():
NOTIFY_EVERY = timedelta(days=180) # users notified recently should be ignored
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/"
try:
r = api.get(path)
@ -181,7 +181,7 @@ def notify_vpn_auth_fails():
@shared_task
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
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):
""" 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)

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

@ -11,7 +11,7 @@ def bwformat(bps):
try:
bps = float(bps)
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)
filesize_number_format = lambda value: formats.number_format(round(value, 1), -1)
@ -23,7 +23,7 @@ def bwformat(bps):
P = 1 * 10 ** 15
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:
value = ugettext("%s Kbps") % filesize_number_format(bps / K)
elif bps < G:

@ -103,12 +103,14 @@ class OnlineStripeTests(BaseOnlineTest):
self.selenium.find_element_by_xpath('//button[@type="submit"]').click()
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():
# refresh payment as we dont have a worker
p = Payment.objects.order_by('id').first()
p = Payment.objects.order_by("id").first()
p.refresh()
self.selenium.refresh()
@ -159,12 +161,14 @@ class OnlineStripeTests(BaseOnlineTest):
self.selenium.find_element_by_xpath('//button[@type="submit"]').click()
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():
# refresh sub as we dont have a worker
p = Subscription.objects.order_by('id').first()
p = Subscription.objects.order_by("id").first()
p.refresh()
self.selenium.refresh()

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

@ -4,18 +4,17 @@ import django_lcore
from . import views
app_name = 'lambdainst'
app_name = "lambdainst"
urlpatterns = [
path('login', auth_views.LoginView.as_view(), name='login'),
path('discourse_login', views.discourse_login, name='discourse_login'),
path('logout', views.logout, name='logout'),
path('signup', views.signup, name='signup'),
path('settings', views.settings, name='account_settings'),
path('config', views.config),
path('config_dl', django_lcore.views.openvpn_dl),
path('wireguard', views.wireguard),
path('wireguard/new', views.wireguard_new, name='wireguard_new'),
path('', views.index, name='index'),
path("login", auth_views.LoginView.as_view(), name="login"),
path("discourse_login", views.discourse_login, name="discourse_login"),
path("logout", views.logout, name="logout"),
path("signup", views.signup, name="signup"),
path("settings", views.settings, name="account_settings"),
path("config", views.config),
path("config_dl", django_lcore.views.openvpn_dl),
path("wireguard", views.wireguard),
path("wireguard/new", views.wireguard_new, name="wireguard_new"),
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.db.models import Count
from django.db import transaction
from django.http import (HttpResponse,
HttpResponseNotFound, HttpResponseRedirect,
JsonResponse)
from django.http import (
HttpResponse,
HttpResponseNotFound,
HttpResponseRedirect,
JsonResponse,
)
from django.shortcuts import redirect, render
from django.utils import timezone
from django.utils.translation import ugettext as _
@ -33,16 +36,18 @@ from . import graphs
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
"""
countries_d = dict(countries)
locations = django_lcore.get_clusters()
for (_), loc in locations:
code = loc['country_code'].upper()
loc['country_name'] = countries_d.get(code, code)
code = loc["country_code"].upper()
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]))
return locations
@ -56,58 +61,61 @@ def log_errors(request, form):
def ca_crt(request):
return HttpResponse(content=project_settings.OPENVPN_CA,
content_type='application/x-x509-ca-cert')
return HttpResponse(
content=project_settings.OPENVPN_CA, content_type="application/x-x509-ca-cert"
)
def logout(request):
auth.logout(request)
return redirect('index')
return redirect("index")
def signup(request):
if request.user.is_authenticated:
return redirect('account:index')
return redirect("account:index")
if request.method != 'POST':
if request.method != "POST":
form = SignupForm()
return render(request, 'ccvpn/signup.html', dict(form=form))
return render(request, "ccvpn/signup.html", dict(form=form))
form = SignupForm(request.POST)
grr = request.POST.get('g-recaptcha-response', '')
grr = request.POST.get("g-recaptcha-response", "")
if captcha_test(grr, request):
request.session['signup_captcha_pass'] = True
elif not request.session.get('signup_captcha_pass'):
request.session["signup_captcha_pass"] = True
elif not request.session.get("signup_captcha_pass"):
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():
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'],
form.cleaned_data['email'],
form.cleaned_data['password'])
user = User.objects.create_user(
form.cleaned_data["username"],
form.cleaned_data["email"],
form.cleaned_data["password"],
)
user.save()
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:
pass
user.vpnuser.campaign = request.session.get('campaign')
user.vpnuser.add_paid_time(timedelta(days=7), 'trial')
user.vpnuser.campaign = request.session.get("campaign")
user.vpnuser.add_paid_time(timedelta(days=7), "trial")
user.vpnuser.save()
user.vpnuser.lcore_sync()
user.backend = 'django.contrib.auth.backends.ModelBackend'
user.backend = "django.contrib.auth.backends.ModelBackend"
auth.login(request, user)
# 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
@ -118,70 +126,73 @@ def discourse_login(request):
if project_settings.DISCOURSE_SSO is not True:
return HttpResponseNotFound()
payload = request.GET.get('sso', '')
signature = request.GET.get('sig', '')
payload = request.GET.get("sso", "")
signature = request.GET.get("sig", "")
expected_signature = hmac.new(sso_secret.encode('utf-8'),
payload.encode('utf-8'),
sha256).hexdigest()
expected_signature = hmac.new(
sso_secret.encode("utf-8"), payload.encode("utf-8"), sha256
).hexdigest()
if signature != expected_signature:
return HttpResponseNotFound()
if request.method == 'POST' and 'email' in request.POST:
if request.method == "POST" and "email" in request.POST:
form = ReqEmailForm(request.POST)
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()
if not request.user.email:
form = ReqEmailForm()
return render(request, 'ccvpn/require_email.html', dict(form=form))
return render(request, "ccvpn/require_email.html", dict(form=form))
try:
payload = base64.b64decode(payload).decode('utf-8')
payload = base64.b64decode(payload).decode("utf-8")
payload_data = dict(parse_qsl(payload))
except (TypeError, ValueError):
return HttpResponseNotFound()
payload_data.update({
'external_id': request.user.id,
'username': request.user.username,
'email': request.user.email,
'require_activation': 'true',
})
payload_data.update(
{
"external_id": request.user.id,
"username": request.user.username,
"email": request.user.email,
"require_activation": "true",
}
)
payload = urlencode(payload_data)
payload = base64.b64encode(payload.encode('utf-8'))
signature = hmac.new(sso_secret.encode('utf-8'), payload, sha256).hexdigest()
payload = base64.b64encode(payload.encode("utf-8"))
signature = hmac.new(sso_secret.encode("utf-8"), payload, sha256).hexdigest()
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)
@login_required
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 = {
'text': _("Awesome VPN! 3€ per month, with a free 7 days trial!"),
'via': 'CCrypto_VPN',
'url': ref_url,
'related': 'CCrypto_VPN,CCrypto_org'
"text": _("Awesome VPN! 3€ per month, with a free 7 days trial!"),
"via": "CCrypto_VPN",
"url": ref_url,
"related": "CCrypto_VPN,CCrypto_org",
}
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
"""
def __getitem__(self, months):
n = int(months) * get_price_float()
c = project_settings.PAYMENTS_CURRENCY[1]
return '%.2f %s' % (n, c)
return "%.2f %s" % (n, c)
context = dict(
title=_("Account"),
@ -189,15 +200,16 @@ def index(request):
twitter_link=twitter_url + urlencode(twitter_args),
subscription=request.user.vpnuser.get_subscription(),
backends=sorted(ACTIVE_BACKENDS.values(), key=lambda x: x.backend_display_name),
subscr_backends=sorted((b for b in ACTIVE_BACKENDS.values()
if b.backend_has_recurring),
key=lambda x: x.backend_id),
default_backend='paypal',
subscr_backends=sorted(
(b for b in ACTIVE_BACKENDS.values() if b.backend_has_recurring),
key=lambda x: x.backend_id,
),
default_backend="paypal",
hcaptcha_site_key=project_settings.HCAPTCHA_SITE_KEY,
price=price_fn(),
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):
@ -206,18 +218,17 @@ def captcha_test(grr, request):
if not project_settings.HCAPTCHA_SITE_KEY:
return True
if api_url == 'TEST' and grr == 'TEST-TOKEN':
if api_url == "TEST" and grr == "TEST-TOKEN":
# FIXME: i'm sorry.
return True
data = dict(secret=project_settings.HCAPTCHA_SECRET_KEY,
response=grr)
data = dict(secret=project_settings.HCAPTCHA_SECRET_KEY, response=grr)
try:
r = requests.post(api_url, data=data)
r.raise_for_status()
d = r.json()
return d.get('success')
return d.get("success")
except (requests.ConnectionError, requests.HTTPError, ValueError):
return False
@ -233,13 +244,29 @@ def make_export_zip(user, name):
gw_cache = {}
def process_wg_peer(item):
keys = {"gateway_port", "id", "local_ipv4", "local_ipv6", "name", "object",
"private_key", "public_key"}
keys = {
"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}
def process_ovpn_sess(item):
keys = {"connect_date", "disconnect_date", "remote", "object", "protocol", "id",
"stats", "tunnel"}
keys = {
"connect_date",
"disconnect_date",
"remote",
"object",
"protocol",
"id",
"stats",
"tunnel",
}
def convert(v):
if isinstance(v, datetime):
@ -277,41 +304,50 @@ def make_export_zip(user, name):
return obj
with z.open(name + "/account.json", "w") as jf:
jf.write(json.dumps({
"username": user.username,
"email": user.email,
"date_joined": user.date_joined.isoformat(),
"expiration": user.vpnuser.expiration.isoformat() if user.vpnuser.expiration else None,
}, indent=2).encode('ascii'))
jf.write(
json.dumps(
{
"username": user.username,
"email": user.email,
"date_joined": user.date_joined.isoformat(),
"expiration": user.vpnuser.expiration.isoformat()
if user.vpnuser.expiration
else None,
},
indent=2,
).encode("ascii")
)
with z.open(name + "/wireguard_peers.json", "w") as jf:
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:
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:
base = django_lcore.api.info['current_instance']
next_page = '/users/' + user.username + '/sessions/'
base = django_lcore.api.info["current_instance"]
next_page = "/users/" + user.username + "/sessions/"
try:
items = django_lcore.api.get(base + next_page).list_iter()
except lcoreapi.APINotFoundError:
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:
items = user.payment_set.all()
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()
return f.getvalue()
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.save()
@ -330,33 +366,37 @@ def deactivate_user(user):
def settings(request):
can_delete = request.user.vpnuser.get_subscription() is None
if request.method != 'POST':
return render(request, 'lambdainst/settings.html', dict(
can_delete=can_delete,
))
if request.method != "POST":
return render(
request,
"lambdainst/settings.html",
dict(
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):
messages.error(request, _("Invalid password"))
return redirect('lambdainst:account_settings')
return redirect("lambdainst:account_settings")
if action == 'email':
email = request.POST.get('email')
if action == "email":
email = request.POST.get("email")
if email:
request.user.email = email
messages.success(request, _("OK! Email address changed."))
else:
request.user.email = ''
request.user.email = ""
messages.success(request, _("OK! Email address unset."))
request.user.save()
return redirect('lambdainst:account_settings')
return redirect("lambdainst:account_settings")
elif action == 'password':
pw = request.POST.get('password')
pw2 = request.POST.get('password2')
elif action == "password":
pw = request.POST.get("password")
pw2 = request.POST.get("password2")
if pw != pw2 or not pw:
messages.error(request, _("Password and confirmation do not match"))
else:
@ -364,95 +404,120 @@ def settings(request):
messages.success(request, _("OK! Password changed."))
request.user.save()
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")
data = make_export_zip(request.user, timestamp)
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
elif action == 'delete' and can_delete:
elif action == "delete" and can_delete:
with transaction.atomic():
deactivate_user(request.user)
logout(request)
messages.success(request, _("OK! Your account has been deactivated."))
return redirect('/')
return render(request, 'lambdainst/settings.html', dict(
title=_("Settings"),
user=request.user,
can_delete=can_delete,
))
return redirect("/")
return render(
request,
"lambdainst/settings.html",
dict(
title=_("Settings"),
user=request.user,
can_delete=can_delete,
),
)
@login_required
def config(request):
return render(request, 'lambdainst/config.html', dict(
title=_("Config"),
config_os=django_lcore.openvpn.CONFIG_OS,
config_countries=(c for _, c in get_locations()),
config_protocols=django_lcore.openvpn.PROTOCOLS,
))
return render(
request,
"lambdainst/config.html",
dict(
title=_("Config"),
config_os=django_lcore.openvpn.CONFIG_OS,
config_countries=(c for _, c in get_locations()),
config_protocols=django_lcore.openvpn.PROTOCOLS,
),
)
def api_locations(request):
def format_loc(cc, l):
msg = ""
tags = l.get('tags', {})
message = tags.get('message')
tags = l.get("tags", {})
message = tags.get("message")
if message:
msg = " [%s]" % message
return {
'country_name': l['country_name'] + msg,
'country_code': cc,
'hostname': l['hostname'],
'bandwidth': l['bandwidth'],
'servers': l['servers'],
"country_name": l["country_name"] + msg,
"country_code": cc,
"hostname": l["hostname"],
"bandwidth": l["bandwidth"],
"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):
locations = get_locations()
ctx = {
'title': _("Servers"),
'locations': locations,
"title": _("Servers"),
"locations": locations,
}
return render(request, 'lambdainst/status.html', ctx)
return render(request, "lambdainst/status.html", ctx)
@user_passes_test(lambda user: user.is_staff)
def admin_status(request):
graph_name = request.GET.get('graph_name')
graph_period = request.GET.get('period')
if graph_period not in ('y', 'm'):
graph_period = 'm'
graph_name = request.GET.get("graph_name")
graph_period = request.GET.get("period")
if graph_period not in ("y", "m"):
graph_period = "m"
if graph_name:
if graph_name == 'users':
if graph_name == "users":
content = graphs.users_graph(graph_period)
elif graph_name == 'payments_paid':
elif graph_name == "payments_paid":
content = graphs.payments_paid_graph(graph_period)
elif graph_name == 'payments_success':
elif graph_name == "payments_success":
content = graphs.payments_success_graph(graph_period)
else:
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, 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 = {
'api_status': {k: str(v) for k, v in django_lcore.api.info.items() if k in lcore_keys},
'payment_backends': sorted(ACTIVE_BACKENDS.values(), key=lambda x: x.backend_id),
'payment_status': payment_status,
"api_status": {
k: str(v) for k, v in django_lcore.api.info.items() if k in lcore_keys
},
"payment_backends": sorted(
ACTIVE_BACKENDS.values(), key=lambda x: x.backend_id
),
"payment_status": payment_status,
}
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)
@ -460,36 +525,40 @@ def admin_ref(request):
last_week = datetime.now() - timedelta(days=7)
last_month = datetime.now() - timedelta(days=30)
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) \
.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]
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)
.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 = {
'top_ref': top_ref,
'top_ref_week': top_ref_week,
'top_ref_month': top_ref_month,
"top_ref": top_ref,
"top_ref_week": top_ref_week,
"top_ref_month": top_ref_month,
}
ctx.update(site.each_context(request))
return render(request, 'lambdainst/admin_ref.html', ctx)
return render(request, "lambdainst/admin_ref.html", ctx)
@login_required
def wireguard(request):
api = django_lcore.api
if request.method == 'POST':
action = request.POST.get('action')
if action == 'delete_key':
key = api.get_wg_peer(request.user.username, request.POST.get('peer_id'))
if request.method == "POST":
action = request.POST.get("action")
if action == "delete_key":
key = api.get_wg_peer(request.user.username, request.POST.get("peer_id"))
if key:
key.delete()
elif action == 'set_name':
key = api.get_wg_peer(request.user.username, request.POST.get('peer_id'))
elif action == "set_name":
key = api.get_wg_peer(request.user.username, request.POST.get("peer_id"))
if key:
name = request.POST.get('name')
name = request.POST.get("name")
if name:
name = name[:21]
key.rename(name)
@ -503,19 +572,19 @@ def wireguard(request):
context = dict(
can_create_key=len(keys) < int(site_config.WIREGUARD_MAX_PEERS),
menu_item='wireguard',
menu_item="wireguard",
enabled=request.user.vpnuser.is_paid,
config_countries=[(k, v) for (k, v) in django_lcore.get_clusters()],
keys=keys,
locations=get_locations(),
)
return render(request, 'lambdainst/wireguard.html', context)
return render(request, "lambdainst/wireguard.html", context)
@login_required
def wireguard_new(request):
if not request.user.vpnuser.is_paid:
return redirect('account:index')
return redirect("account:index")
try:
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)
keys = []
if len(keys) >= int(site_config.WIREGUARD_MAX_PEERS):
return redirect('/account/wireguard')
return redirect("/account/wireguard")
api = django_lcore.api
if request.method == 'POST':
action = request.POST.get('action')
if request.method == "POST":
action = request.POST.get("action")
form = WgPeerForm(request.POST)
if action == 'add_key':
if action == "add_key":
if form.is_valid():
api.create_wg_peer(
request.user.username,
public_key=form.cleaned_data['public_key'],
name=form.cleaned_data['name'],
public_key=form.cleaned_data["public_key"],
name=form.cleaned_data["name"],
)
else:
log_errors(request, form)
return redirect('/account/wireguard')
return redirect("/account/wireguard")
context = dict(
menu_item='wireguard',
menu_item="wireguard",
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]]
name = "black"
version = "20.8b1"
version = "21.7b0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.6.2"
[package.dependencies]
appdirs = "*"
click = ">=7.1.2"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.6,<1"
pathspec = ">=0.8.1,<1"
regex = ">=2020.1.8"
toml = ">=0.10.1"
typed-ast = ">=1.4.0"
typing-extensions = ">=3.7.4"
tomli = ">=0.2.6,<2.0.0"
typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""}
typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
[package.extras]
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]]
name = "cached-property"
@ -408,18 +410,18 @@ jsmin = "*"
[[package]]
name = "flower"
version = "0.9.5"
version = "1.0.0"
description = "Celery Flower"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
celery = {version = ">=4.3.0", markers = "python_version >= \"3.7\""}
celery = ">=5.0.5"
humanize = "*"
prometheus-client = "0.8.0"
prometheus-client = ">=0.8.0"
pytz = "*"
tornado = {version = ">=5.0.0,<7.0.0", markers = "python_version >= \"3.5.2\""}
tornado = ">=5.0.0,<7.0.0"
[[package]]
name = "humanize"
@ -499,16 +501,17 @@ python-versions = "*"
[[package]]
name = "isort"
version = "5.8.0"
version = "5.9.2"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.6,<4.0"
python-versions = ">=3.6.1,<4.0"
[package.extras]
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
[[package]]
name = "jedi"
@ -682,22 +685,22 @@ python-versions = "*"
[[package]]
name = "prometheus-client"
version = "0.8.0"
version = "0.11.0"
description = "Python client for the Prometheus monitoring system."
category = "main"
optional = false
python-versions = "*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.extras]
twisted = ["twisted"]
[[package]]
name = "prompt-toolkit"
version = "3.0.3"
version = "3.0.19"
description = "Library for building powerful interactive command lines in Python"
category = "main"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.6.1"
[package.dependencies]
wcwidth = "*"
@ -782,14 +785,6 @@ python-versions = "*"
[package.dependencies]
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]]
name = "python-crontab"
version = "2.5.1"
@ -930,6 +925,14 @@ category = "dev"
optional = false
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]]
name = "tornado"
version = "6.1"
@ -1020,7 +1023,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = ">=3.7,<4.0"
content-hash = "80258fdb7e8f9b695a3a5ad92efb52c47433243bccc905d701d1e377f38877d9"
content-hash = "5e7e869eb1d76bb7621e1653d293566a4ad761932fcc33e74c88602554c6d7f4"
[metadata.files]
amqp = [
@ -1052,8 +1055,8 @@ billiard = [
{file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"},
]
black = [
{file = "black-20.8b1-py3-none-any.whl", hash = "sha256:70b62ef1527c950db59062cda342ea224d772abdf6adc58b86a45421bab20a6b"},
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
{file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"},
{file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"},
]
cached-property = [
{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"},
]
flower = [
{file = "flower-0.9.5-py2.py3-none-any.whl", hash = "sha256:71be02bff7b2f56b0a07bd947fb3c748acba7f44f80ae88125d8954ce1a89697"},
{file = "flower-0.9.5.tar.gz", hash = "sha256:56916d1d2892e25453d6023437427fc04706a1308e0bd4822321da34e1643f9c"},
{file = "flower-1.0.0-py2.py3-none-any.whl", hash = "sha256:a4fcf959881135303e98a74cc7533298b7dfeb48abcd1d90c5bd52cb789430a8"},
{file = "flower-1.0.0.tar.gz", hash = "sha256:2e17c4fb55c569508f3bfee7fe41f44b8362d30dbdf77b604a9d9f4740fe8cbd"},
]
humanize = [
{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"},
]
isort = [
{file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"},
{file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"},
{file = "isort-5.9.2-py3-none-any.whl", hash = "sha256:eed17b53c3e7912425579853d078a0832820f023191561fcee9d7cae424e0813"},
{file = "isort-5.9.2.tar.gz", hash = "sha256:f65ce5bd4cbc6abdfbe29afc2f0245538ab358c14590912df638033f157d555e"},
]
jedi = [
{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"},
]
prometheus-client = [
{file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"},
{file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"},
{file = "prometheus_client-0.11.0-py2.py3-none-any.whl", hash = "sha256:b014bc76815eb1399da8ce5fc84b7717a3e63652b0c0f8804092c9363acab1b2"},
{file = "prometheus_client-0.11.0.tar.gz", hash = "sha256:3a8baade6cb80bcfe43297e33e7623f3118d660d41387593758e2fb1ea173a86"},
]
prompt-toolkit = [
{file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"},
{file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"},
{file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"},
{file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"},
]
psycopg2-binary = [
{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-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 = [
{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.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 = [
{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"},

@ -13,7 +13,6 @@ markdown = "^3.1"
requests = "^2.21"
pygal = "^2.4"
pytz = "^2021.1"
python-bitcoinlib = "^0.11"
stripe = "^2.24"
django-constance = {version = "^2.7",extras = ["database"]}
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"
django-celery-beat = "^2.0.0"
redis = "^3.5.3"
flower = "^0.9.5"
flower = "^1"
django-extensions = "^3.1.3"
[tool.poetry.dev-dependencies]
pylint = "^2.3"
pylint-django = "^2.0.14"
black = {version = "^20.8b1", allow-prereleases = true}
black = {version = "^21.7b0", allow-prereleases = true}
selenium = "^3.141.0"
ipython = "^7.25.0"

Loading…
Cancel
Save