Changeset - dd827e1dedde
[Not reviewed]
Dennis Fink - 9 years ago 2016-05-16 14:15:47
dennis.fink@c3l.lu
Fix IndexError
2 files changed with 7 insertions and 1 deletions:
0 comments (0 inline, 0 general)
ennstatus/api/model.py
Show inline comments
 
@@ -142,119 +142,122 @@ class Server:
 
                default_weights
 
            )
 
            self.mean_guard_probability = kwargs.get(
 
                'mean_guard_probability',
 
                default_weights
 
            )
 
            self.mean_exit_probability = kwargs.get(
 
                'mean_exit_probability',
 
                default_weights
 
            )
 
            self.mean_consensus_weight_fraction = kwargs.get(
 
                'mean_consensus_weight_fraction',
 
                default_weights
 
            )
 
            self.mean_middle_probability = kwargs.get(
 
                'mean_middle_probability',
 
                default_weights
 
            )
 

	
 
    @classmethod
 
    def from_file_by_name(cls, name):
 

	
 
        filepath = Path('data') / (name.lower() + '.json')
 

	
 
        current_app.logger.info('Loading {}'.format(str(filepath)))
 
        if filepath.exists() and filepath.is_file():
 
            try:
 
                with filepath.open(encoding='utf-8') as f:
 
                    data = json.load(f, cls=ServerDecoder)
 
            except (IOError, ValueError):
 
                current_app.logger.error('IOError or ValueError')
 
                return False
 
            else:
 
                return cls(**data)
 
        else:
 
            current_app.logger.error('File error!')
 
            return False
 

	
 
    @classmethod
 
    def from_json(cls, server):
 

	
 
        try:
 
            if cls.check_json_format(json.loads(server)):
 
                decoded = json.loads(server, cls=ServerDecoder)
 
                return cls(**decoded)
 
        except (jsonschema.ValidationError, ValueError) as e:
 
            raise e
 

	
 
    @classmethod
 
    def from_dict(cls, server):
 
        return cls.from_json(json.dumps(server))
 

	
 
    def json(self):
 
        return json.dumps(self.__dict__, cls=ServerEncoder)
 

	
 
    @staticmethod
 
    def check_json_format(server):
 

	
 
        try:
 
            validate(server)
 
        except jsonschema.ValidationError as e:
 
            raise e
 

	
 
        for key in ('ip', 'ip6'):
 
            if key in server:
 
                address = ipaddress.ip_address(server[key])
 
                if not check_ip(address):
 
                    raise ValueError('{} is not accepted!\n'.format(key))
 
        return True
 

	
 
    def save(self):
 

	
 
        filepath = Path('data') / (self.name.lower() + '.json')
 

	
 
        try:
 
            with filepath.open(mode='w', encoding='utf-8') as f:
 
                json.dump(self.__dict__, f, cls=ServerEncoder)
 
        except Exception as e:
 
            raise e
 

	
 
    def update_weights(self):
 

	
 
        if self.type not in ('exit', 'relay'):
 
            raise NotImplementedError
 

	
 
        url = 'https://onionoo.torproject.org/weights?lookup={}'.format(
 
            self.fingerprint
 
        )
 

	
 
        data = requests.get(url)
 

	
 
        try:
 
            data.raise_for_status()
 
        except requests.HTTPError as e:
 
            raise e
 
        else:
 
            data = data.json()['relays'][0]
 
            try:
 
                data = data.json()['relays'][0]
 
            except IndexError as e:
 
                raise RuntimeError from e
 

	
 
        self.mean_consensus_weight = calculate_weight(data['consensus_weight'])
 
        self.mean_exit_probability = calculate_weight(data['exit_probability'])
 
        self.mean_guard_probability = calculate_weight(
 
            data['guard_probability']
 
        )
 
        self.mean_middle_probability = calculate_weight(
 
            data['middle_probability']
 
        )
 
        self.mean_consensus_weight_fraction = calculate_weight(
 
            data['consensus_weight_fraction']
 
        )
 

	
 
    def check_status(self):
 

	
 
        now = datetime.utcnow()
 
        delta = now - self.last_updated
 

	
 
        if delta.seconds >= 3600:
 
            self.status = False
 
        elif delta.seconds >= 600:
 
            self.status = None
ennstatus/api/views.py
Show inline comments
 
@@ -30,165 +30,168 @@ import requests
 

	
 
from ennstatus import csrf
 
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)
 

	
 

	
 
@csrf.exempt
 
@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
 
    except RuntimeError 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'}
0 comments (0 inline, 0 general)