You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
5.3 KiB
Python

from datetime import timedelta, datetime
import lcoreapi
from django.conf import settings
import logging
cluster_messages = settings.LAMBDAINST_CLUSTER_MESSAGES
lcore_settings = settings.LCORE
LCORE_BASE_URL = lcore_settings.get('BASE_URL')
LCORE_API_KEY = lcore_settings['API_KEY']
LCORE_API_SECRET = lcore_settings['API_SECRET']
LCORE_SOURCE_ADDR = lcore_settings.get('SOURCE_ADDRESS')
LCORE_INST_SECRET = lcore_settings['INST_SECRET']
LCORE_TIMEOUT = lcore_settings.get('TIMEOUT', 10)
# The default is to log the exception and only raise it if we cannot show
# the previous value or a default value instead.
LCORE_RAISE_ERRORS = bool(lcore_settings.get('RAISE_ERRORS', False))
LCORE_CACHE_TTL = lcore_settings.get('CACHE_TTL', 60)
if isinstance(LCORE_CACHE_TTL, int):
LCORE_CACHE_TTL = timedelta(seconds=LCORE_CACHE_TTL)
assert isinstance(LCORE_CACHE_TTL, timedelta)
VPN_AUTH_STORAGE = settings.VPN_AUTH_STORAGE
assert VPN_AUTH_STORAGE in ('core', 'inst')
core_api = lcoreapi.API(LCORE_API_KEY, LCORE_API_SECRET, LCORE_BASE_URL,
timeout=LCORE_TIMEOUT)
class APICache:
""" Cache data for a time, try to update and silence errors.
Outdated data is not a problem.
"""
def __init__(self, ttl=None, initial=None):
self.cache_date = datetime.fromtimestamp(0)
self.ttl = ttl or LCORE_CACHE_TTL
self.has_cached_value = initial is not None
self.cached = initial() if initial else None
def query(self, wrapped, *args, **kwargs):
try:
return wrapped(*args, **kwargs)
except lcoreapi.APIError:
logger = logging.getLogger('django.request')
logger.exception("core api error")
if LCORE_RAISE_ERRORS:
raise
if not self.has_cached_value:
# We only return a default value if we were given one.
# Prevents returning an unexpected None.
raise
# Return previous value
return self.cached
def __call__(self, wrapped):
def wrapper(*args, **kwargs):
if self.cache_date > (datetime.now() - self.ttl):
return self.cached
self.cached = self.query(wrapped, *args, **kwargs)
# New results *and* errors are cached
self.cache_date = datetime.now()
return self.cached
return wrapper
@APICache(initial=lambda: 0)
def current_active_sessions():
return core_api.get(core_api.info['current_instance'] + '/sessions', active=True)['total_count']
@APICache(initial=lambda: [])
def get_locations():
gateways = core_api.get('/gateways/', enabled=True)
locations = {}
for gw in gateways.list_iter():
cc = gw['cluster_name']
if cc not in locations:
locations[cc] = dict(
servers=0,
bandwidth=0,
hostname='gw.' + cc + '.204vpn.net',
country_code=cc,
message=cluster_messages.get(cc),
)
locations[cc]['servers'] += 1
locations[cc]['bandwidth'] += gw['bandwidth']
locations = sorted(locations.items(), key=lambda x: x[1]['country_code'])
return locations
@APICache(initial=lambda: [])
def get_gateway_exit_ips():
gateways = core_api.get('/gateways/', enabled=True)
ipv4_list = []
ipv6_list = []
for gw in gateways.list_iter():
ma = gw['main_addr']
if ma.get('ipv4'):
ipv4_list.append(ma['ipv4'])
if ma.get('ipv6'):
ipv6_list.append(ma['ipv6'])
# TODO: IPv6 support
return ipv4_list
def is_vpn_gateway(ip):
addresses = get_gateway_exit_ips()
return ip in addresses
def create_user(username, cleartext_password):
""" The password will be hashed and stored safely on the core,
so we have to send it clearly here.
"""
path = core_api.info['current_instance'] + '/users/'
core_api.post(path, data={
'username': username,
'password': cleartext_password,
'expiration_date': datetime(1, 1, 1).isoformat(), # Expired.
})
def update_user_expiration(user):
path = core_api.info['current_instance'] + '/users/' + user.username
try:
if not user.is_active:
core_api.patch(path, data={
'expiration_date': datetime(1, 1, 1).isoformat(), # Expired.
})
return
core_api.patch(path, data={
'expiration_date': user.vpnuser.expiration,
})
except lcoreapi.APIError:
# User can't do anything to this, we should just report it
logger = logging.getLogger('django.request')
logger.exception("core api error, missing user (exp update)")
def update_user_password(user, cleartext_password):
path = core_api.info['current_instance'] + '/users/' + user.username
try:
core_api.patch(path, data={
'password': cleartext_password,
})
except lcoreapi.APINotFoundError:
# This time we can try fix it!
create_user(user.username, cleartext_password)
except lcoreapi.APIError:
# and maybe fail.
logger = logging.getLogger('django.request')
logger.exception("core api error (password update)")
def delete_user(username):
path = core_api.info['current_instance'] + '/users/' + username
core_api.delete(path)