app.py 3.9 KB
Newer Older
okhin's avatar
okhin committed
1
2
3
#!/usr/bin/env python

import sqlite3
okhin's avatar
okhin committed
4
5
6
import datetime
import uuid
import json
okhin's avatar
okhin committed
7
8
9
10
11
12
13

from bottle import route, run, request, abort, install, get, post
from bottle_sqlite import SQLitePlugin
import jwt

install(SQLitePlugin(dbfile='call.db'))

okhin's avatar
okhin committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def sanitize_phonenumber(number):
    """
    This function is used to sanitize a phone number.
    If it starts with a +, it will be removed and replaced by 00.
    Any chars who do notbelong to [0-9] will be stripped.
    If the number doesn't starts with 00, a TypeError will be raised
    """
    if number[0] == '+':
        number = '00' + number[1:]
    number = ''.join([c for c in number if c in '0123456789'])
    if not number.startswith('00'):
        raise TypeError('{} is not a valid international number, it should start with 00')
    return number

class Call(json.JSONEncoder):
    """
    This Class is used to manage operatiosn on a call, to print it and dump it.
    """
    history = {}
    def __init__(self, caller, callee, owner):
        try:
            self.caller = caller
            self.callee = callee
            self.owner = owner
            self.id = uuid.uuid4()
        except Exception as e:
            raise e

    def url(self):
        return ''.join(['/calls/', self.id])

    def default(self, o):
        """
        We need to implement this to be able to JSONEncode
        """
        return {  'caller': self.caller
                , 'callee': self.callee
                , 'callid': self.callee
                , 'url': self.url()
                , 'history': self.history
                , 'owner': self.owner }

    @classmethod
    def load(cls, callid, db):
        results = db.execute('SELECT caller, callee, owner, callid, history, state FROM calls WHERE callid = ?;', (callid,))
        try:
            result = results.fetchone()
            object = cls(result[0], result[1], result[2])
            object.id = result[3]
            object.history = json.loads(result[4])
            return object
        except:
            return None

okhin's avatar
okhin committed
68
69
70
71
72
73
74
75
76
77
# We need a decorator to check if our query is authenticated.
# We will store an API key and SECRET in ur database, the client
# needs to have both of them.
# He must then send us a JWT token with an API claim in the payload.
# The JWT token must be encoded and signed with the SECRET. If the
# token is bad, we return a 403.
def authenticated(f):
    def wrapped(db, *args, **kwargs):
        # Let's get the JWT token. It should be a params (from get or post or whatev')
        if 'token' not in request.params:
okhin's avatar
okhin committed
78
            abort(401, "No token found in the query")
okhin's avatar
okhin committed
79
80
        # We want the api id in the params to.
        if 'api' not in request.params:
okhin's avatar
okhin committed
81
            abort(401, "No api id found in the params")
okhin's avatar
okhin committed
82
83
84
85
86
87
88
        # Now, let's get the token on our side
        try:
            results = db.execute('SELECT token FROM users WHERE api = ?', (request.params['api'],)).fetchall()
            assert len(results) == 1
            token = results[0][0]
            auth_token = jwt.decode(request.params['token'], token)
            assert auth_token['api'] == request.params['api']
okhin's avatar
okhin committed
89
90
            for key in auth_token:
                request.params[key] = auth_token[key]
okhin's avatar
okhin committed
91
92
93
94
95
96
97
        except (jwt.exceptions.InvalidTokenError, AssertionError) as e:
            abort(403, e)
        except Exception as e:
            abort(500, e)
        return f(db, *args, **kwargs)
    return wrapped

okhin's avatar
okhin committed
98
@get('/calls/')
okhin's avatar
okhin committed
99
@authenticated
okhin's avatar
okhin committed
100
101
102
103
104
105
106
107
108
109
110
def calls(db):
    """
    Return the list of calls associated to the connected user.
    The call has a status, caller, callee and history (status change+timestamp)
    """
    results = db.execute('SELECT callid FROM calls WHERE owner = ?;', (request.params['api'],))
    calls = []
    for call in results.fetchall():
        calls.append(Calls.load(call[0], db))
    head = {'call': '/calls/', 'user': request.params['api'], 'hits': len(calls)}
    return {'head': head, 'data': calls}
okhin's avatar
okhin committed
111
112
    
run(host='localhost', port=8080, debug=True)