|
|
@ -12,6 +12,7 @@ from django.contrib.admin.sites import site
|
|
|
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
|
|
|
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.http import (HttpResponse,
|
|
|
|
from django.http import (HttpResponse,
|
|
|
|
HttpResponseNotFound, HttpResponseRedirect,
|
|
|
|
HttpResponseNotFound, HttpResponseRedirect,
|
|
|
|
JsonResponse)
|
|
|
|
JsonResponse)
|
|
|
@ -177,7 +178,7 @@ def index(request):
|
|
|
|
if b.backend_has_recurring),
|
|
|
|
if b.backend_has_recurring),
|
|
|
|
key=lambda x: x.backend_id),
|
|
|
|
key=lambda x: x.backend_id),
|
|
|
|
default_backend='paypal',
|
|
|
|
default_backend='paypal',
|
|
|
|
recaptcha_site_key=project_settings.RECAPTCHA_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,
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -185,14 +186,13 @@ def index(request):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def captcha_test(grr, request):
|
|
|
|
def captcha_test(grr, request):
|
|
|
|
api_url = project_settings.RECAPTCHA_API
|
|
|
|
api_url = project_settings.HCAPTCHA_API
|
|
|
|
|
|
|
|
|
|
|
|
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.RECAPTCHA_SECRET_KEY,
|
|
|
|
data = dict(secret=project_settings.HCAPTCHA_SECRET_KEY,
|
|
|
|
remoteip=get_client_ip(request),
|
|
|
|
|
|
|
|
response=grr)
|
|
|
|
response=grr)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
@ -220,30 +220,170 @@ def trial(request):
|
|
|
|
return redirect('account:index')
|
|
|
|
return redirect('account:index')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_export_zip(user, name):
|
|
|
|
|
|
|
|
import io
|
|
|
|
|
|
|
|
import zipfile
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
f = io.BytesIO()
|
|
|
|
|
|
|
|
z = zipfile.ZipFile(f, mode="w")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gw_cache = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_wg_peer(item):
|
|
|
|
|
|
|
|
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"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def convert(v):
|
|
|
|
|
|
|
|
if isinstance(v, datetime):
|
|
|
|
|
|
|
|
return v.isoformat()
|
|
|
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
obj = {k: convert(v) for (k, v) in item.items() if k in keys}
|
|
|
|
|
|
|
|
gw_url = item["gateway"]["href"]
|
|
|
|
|
|
|
|
if gw_url not in gw_cache:
|
|
|
|
|
|
|
|
gw = django_lcore.api.get(gw_url)
|
|
|
|
|
|
|
|
gw_cache[gw_url] = {
|
|
|
|
|
|
|
|
"name": gw["name"],
|
|
|
|
|
|
|
|
"ipv4": gw["main_ipv4"],
|
|
|
|
|
|
|
|
"ipv6": gw["main_ipv6"],
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
obj["gateway"] = gw_cache[gw_url]
|
|
|
|
|
|
|
|
return obj
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_payments(item):
|
|
|
|
|
|
|
|
obj = {
|
|
|
|
|
|
|
|
"id": item.id,
|
|
|
|
|
|
|
|
"backend": item.backend_id,
|
|
|
|
|
|
|
|
"status": item.status,
|
|
|
|
|
|
|
|
"created": item.created.isoformat(),
|
|
|
|
|
|
|
|
"confirmed": item.confirmed_on.isoformat() if item.confirmed_on else None,
|
|
|
|
|
|
|
|
"amount": item.amount / 100,
|
|
|
|
|
|
|
|
"paid_amount": item.amount / 100,
|
|
|
|
|
|
|
|
"time": item.time.total_seconds(),
|
|
|
|
|
|
|
|
"external_id": item.backend_extid,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if item.subscription:
|
|
|
|
|
|
|
|
obj["subscription"] = item.subscription.backend_extid
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
obj["subscription"] = None
|
|
|
|
|
|
|
|
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'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
with z.open(name + "/wireguard_peers.json", "w") as jf:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
keys = 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'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
with z.open(name + "/openvpn_logs.json", "w") as jf:
|
|
|
|
|
|
|
|
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'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
z.close()
|
|
|
|
|
|
|
|
return f.getvalue()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def deactivate_user(user):
|
|
|
|
|
|
|
|
""" clear most information from a user, keeping the username and id """
|
|
|
|
|
|
|
|
user.vpnuser.clear_fields()
|
|
|
|
|
|
|
|
user.vpnuser.save()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
user.is_active = False
|
|
|
|
|
|
|
|
user.email = ""
|
|
|
|
|
|
|
|
user.password = ""
|
|
|
|
|
|
|
|
user.save()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
user.giftcodeuser_set.all().delete()
|
|
|
|
|
|
|
|
user.payment_set.update(backend_data="null")
|
|
|
|
|
|
|
|
user.subscription_set.update(backend_data="null")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
django_lcore.sync_user(user.vpnuser)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
@login_required
|
|
|
|
def settings(request):
|
|
|
|
def settings(request):
|
|
|
|
|
|
|
|
can_delete = request.user.vpnuser.get_subscription() is None
|
|
|
|
|
|
|
|
|
|
|
|
if request.method != 'POST':
|
|
|
|
if request.method != 'POST':
|
|
|
|
return render(request, 'lambdainst/settings.html')
|
|
|
|
return render(request, 'lambdainst/settings.html', dict(
|
|
|
|
|
|
|
|
can_delete=can_delete,
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
action = request.POST.get('action')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 = ''
|
|
|
|
|
|
|
|
messages.success(request, _("OK! Email address unset."))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
request.user.save()
|
|
|
|
|
|
|
|
return redirect('lambdainst:account_settings')
|
|
|
|
|
|
|
|
|
|
|
|
pw = request.POST.get('password')
|
|
|
|
elif action == 'password':
|
|
|
|
pw2 = request.POST.get('password2')
|
|
|
|
pw = request.POST.get('password')
|
|
|
|
if pw and pw2:
|
|
|
|
pw2 = request.POST.get('password2')
|
|
|
|
if pw != pw2:
|
|
|
|
if pw != pw2 or not pw:
|
|
|
|
messages.error(request, _("Passwords do not match"))
|
|
|
|
messages.error(request, _("Password and confirmation do not match"))
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
request.user.set_password(pw)
|
|
|
|
request.user.set_password(pw)
|
|
|
|
django_lcore.sync_user(request.user.vpnuser)
|
|
|
|
django_lcore.sync_user(request.user.vpnuser)
|
|
|
|
messages.success(request, _("OK!"))
|
|
|
|
messages.success(request, _("OK! Password changed."))
|
|
|
|
|
|
|
|
request.user.save()
|
|
|
|
email = request.POST.get('email')
|
|
|
|
return redirect('lambdainst:account_settings')
|
|
|
|
if email:
|
|
|
|
|
|
|
|
request.user.email = email
|
|
|
|
elif action == 'export':
|
|
|
|
else:
|
|
|
|
timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
|
|
|
request.user.email = ''
|
|
|
|
data = make_export_zip(request.user, timestamp)
|
|
|
|
|
|
|
|
r = HttpResponse(content=data, content_type="application/zip")
|
|
|
|
request.user.save()
|
|
|
|
r["Content-Disposition"] = 'attachment; filename="ccvpn-export-{}-{}.zip"'.format(request.user.username, timestamp)
|
|
|
|
|
|
|
|
return r
|
|
|
|
return render(request, 'lambdainst/settings.html', dict(title=_("Settings")))
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
@login_required
|
|
|
@ -263,31 +403,6 @@ def gift_code(request):
|
|
|
|
return redirect('account:index')
|
|
|
|
return redirect('account:index')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
|
|
|
|
def logs(request):
|
|
|
|
|
|
|
|
page_size = 20
|
|
|
|
|
|
|
|
page = int(request.GET.get('page', 0))
|
|
|
|
|
|
|
|
offset = page * page_size
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
base = django_lcore.api.info['current_instance']
|
|
|
|
|
|
|
|
path = '/users/' + request.user.username + '/sessions/'
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
l = django_lcore.api.get(base + path, offset=offset, limit=page_size)
|
|
|
|
|
|
|
|
total_count = l['total_count']
|
|
|
|
|
|
|
|
items = l['items']
|
|
|
|
|
|
|
|
except lcoreapi.APINotFoundError:
|
|
|
|
|
|
|
|
total_count = 0
|
|
|
|
|
|
|
|
items = []
|
|
|
|
|
|
|
|
return render(request, 'lambdainst/logs.html', {
|
|
|
|
|
|
|
|
'sessions': items,
|
|
|
|
|
|
|
|
'page': page,
|
|
|
|
|
|
|
|
'prev': page - 1 if page > 0 else None,
|
|
|
|
|
|
|
|
'next': page + 1 if offset + page_size < total_count else None,
|
|
|
|
|
|
|
|
'last_page': total_count // page_size,
|
|
|
|
|
|
|
|
'title': _("Logs"),
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
@login_required
|
|
|
|
def config(request):
|
|
|
|
def config(request):
|
|
|
|
return render(request, 'lambdainst/config.html', dict(
|
|
|
|
return render(request, 'lambdainst/config.html', dict(
|
|
|
|