#!/usr/bin/env python import sqlite3 import datetime import uuid import json from bottle import route, run, request, abort, install, get, post from bottle_sqlite import SQLitePlugin import jwt import Asterisk install(SQLitePlugin(dbfile='call.db')) 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 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 def save(self, db): ''' Save the Call to database. ''' db.execute('''INSERT OR REPLACE INTO calls (caller, callee, owner, callid, history) VALUES (?, ?, ?, ?, ?) ''', (self.caller, self.callee, self.owner, self.callid, json.dumps(self.history))) # 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: abort(401, "No token found in the query") # We want the api id in the params to. if 'api' not in request.params: abort(401, "No api id found in the params") # 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'] for key in auth_token: request.params[key] = auth_token[key] except (jwt.exceptions.InvalidTokenError, AssertionError) as e: abort(403, e) except Exception as e: abort(500, e) return f(db, *args, **kwargs) return wrapped @get('/calls/') @authenticated 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} run(host='localhost', port=8080, debug=True)