Files @ 20c97cbcdaa8
Branch filter:

Location: FVDE/ennstatus/ennstatus/api/views.py

Dennis Fink
Merged version_5 into default
import ipaddress
import json

from datetime import datetime

from flask import (Blueprint, request, current_app, jsonify, render_template,
                   abort)

from werkzeug.exceptions import BadRequest

import strict_rfc3339
import pygeoip
import requests

from ennstatus.status.functions import (single_server, all_servers,
                                        all_servers_by_type)
from .model import Server
from .auth import httpauth

api_page = Blueprint('api', __name__)
gi4 = pygeoip.GeoIP('/usr/share/GeoIP/GeoIP.dat', pygeoip.MEMORY_CACHE)
gi6 = pygeoip.GeoIP('/usr/share/GeoIP/GeoIPv6.dat', pygeoip.MEMORY_CACHE)


@api_page.route('/update', methods=('POST',))
@httpauth.login_required
def update():

    current_app.logger.info('Handling update')

    try:
        servers = current_app.config['ENNSTATUS_SERVERS']
    except KeyError as e:
        current_app.logger.error(str(e))
        return abort(500)

    username = httpauth.username()

    try:
        if request.remote_addr not in servers[username]['IPS']:
            current_app.logger.warn(
                'Unallowed IP {} tried to update data!'.format(
                    request.remote_addr
                )
            )
            return 'IP not allowed!\n', 403, {'Content-Type': 'text/plain'}
    except KeyError as e:
        current_app.logger.error(str(e))
        return abort(500)

    try:
        data = request.get_json()
    except BadRequest:
        current_app.logger.info('No JSON data supplied!')
        return 'No JSON data supplied!\n', 400, {'Content-Type': 'text/plain'}

    try:
        if username != data['name'].lower():
            current_app.logger.warn(
                'Unallowed user {} tried to update {}!'.format(
                    username, data['name']
                )
            )
            return ('You are not allowed to update this server\n',
                    403, {'Content-Type': 'text/plain'})
    except KeyError:
        return abort(409)

    if 'ip' in data:
        ip = data['ip']
    elif 'ip6' in data:
        ip = data['ip6']
    else:
        ip = request.remote_addr
        try:
            temp_ip = ipaddress.ip_address(ip)
        except ipaddress.AddressValueError:
            return 'IP not allowed!\n', 403, {'Content-Type': 'text/plain'}
        else:
            if temp_ip.version == 4:
                data['ip'] = ip
            elif temp_ip.verison == 6:
                data['ip6'] = ip

    try:
        country = gi4.country_name_by_addr(ip)
    except pygeoip.GeoIPError:
        country = gi6.country_name_by_addr(ip)

    data['country'] = country
    data['last_updated'] = strict_rfc3339.timestamp_to_rfc3339_utcoffset(
        datetime.utcnow().timestamp()
    )

    try:
        server = Server.from_dict(data)
    except Exception as e:
        current_app.logger.warning(' '.join([str(e), str(data)]))
        return str(e), 409, {'Content-Type': 'text/plain'}

    try:
        server.update_weights()
    except NotImplementedError:
        pass
    except requests.HTTPError as e:
        current_app.logger.error(str(e), exc_info=True)
        pass

    try:
        server.save()
    except Exception as e:
        current_app.logger.error(str(e))
        return str(e), 500, {'Content-Type': 'text/plain'}

    current_app.logger.info('Return result')
    return (
        server.json(), 201,
        {
            'Location': '/api/export/json/single?server_name={}'.format(
                server.name
            )
        }
    )


@api_page.route('/export', defaults={'server_type': 'all'})
@api_page.route(('/export/<any("all", "exit", "bridge", "relay", "single")'
                 ':server_type>'))
def export(server_type):

    current_app.logger.info('Handling export')
    if server_type == 'single':
        server_name = request.args.get('server_name', None)
        if server_name is not None:
            server = single_server(server_name)
            if server:
                return server.json(), 200, {'Content-Type': 'application/json'}
            else:
                current_app.logger.warning('Server not found!')
                return ('Server not found!\n',
                        404, {'Content-Type': 'text/plain'})
        else:
            current_app.logger.warning('No server_name specified!')
            return ('No server_name specified!\n',
                    400, {'Content-Type': 'text/plain'})

    else:
        if server_type == 'all':
            current_app.logger.info('Getting all servers!')
            servers = [
                json.loads(
                    server.json()
                ) for server in all_servers()
            ]
        else:
            current_app.logger.info('Getting all {}!'.format(server_type))
            servers = [
                json.loads(
                    server.json()
                ) for server in all_servers_by_type(server_type.lower())
            ]

        return json.dumps(servers), 200, {'Content-Type': 'application/json'}


@api_page.route('/fingerprints', defaults={'server_type': 'all'})
@api_page.route('/fingerprints/<any("all", "exit", "relay"):server_type>')
def fingerprint(server_type):
    if server_type == 'all':
        servers = [server.fingerprint for server in all_servers()
                   if server.type != 'bridge']
    else:
        servers = [server.fingerprint
                   for server in all_servers_by_type(server_type.lower())]

    return '\n'.join(servers), 200, {'Content-Type': 'text/plain'}