finished client request validation, started daemon queue, and added unit tests file

master
Kevin Froman 2018-01-02 02:43:29 -06:00
parent 1fbd6eb64e
commit bb0ff04675
No known key found for this signature in database
GPG Key ID: 0D414D0FE405B63B
4 changed files with 115 additions and 12 deletions

49
api.py
View File

@ -14,19 +14,27 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
''' '''
import flask import flask
from flask import request, Response from flask import request, Response, abort
import configparser, sys, random, threading import configparser, sys, random, threading, hmac, hashlib, base64
from core import Core
''' '''
Main API Main API
''' '''
class API: class API:
def validateToken(self, token):
if self.clientToken != token:
return False
else:
return True
def __init__(self, config, debug): def __init__(self, config, debug):
self.config = config self.config = config
self.debug = debug self.debug = debug
app = flask.Flask(__name__) app = flask.Flask(__name__)
bindPort = int(self.config['CLIENT']['PORT']) bindPort = int(self.config['CLIENT']['PORT'])
clientToken = self.config['CLIENT']['CLIENT HMAC'] self.bindPort = bindPort
self.clientToken = self.config['CLIENT']['CLIENT HMAC']
if not debug: if not debug:
hostNums = [random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)] hostNums = [random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)]
@ -47,20 +55,35 @@ class API:
resp.headers['x-frame-options'] = 'deny' resp.headers['x-frame-options'] = 'deny'
return resp return resp
@app.route('/client/hello') @app.route('/client/')
def hello_world(): def private_handler():
# we should keep a hash DB of requests (with hmac) to prevent replays
action = request.args.get('action')
#if not self.debug:
token = request.args.get('token')
if not self.validateToken(token):
abort(403)
self.validateHost() self.validateHost()
resp = Response('Hello, World!' + request.host) if action == 'hello':
resp = Response('Hello, World! ' + request.host)
elif action == 'stats':
resp =Response('something')
else:
resp = Response('(O_o) Dude what? (invalid command)')
return resp return resp
@app.errorhandler(404) @app.errorhandler(404)
def notfound(err): def notfound(err):
resp = Response("\_(0_0)_/ I got nothin") resp = Response("\_(0_0)_/ I got nothin")
resp.headers = getHeaders(resp) #resp.headers = getHeaders(resp)
return resp
@app.errorhandler(403)
def authFail(err):
resp = Response("Auth required/security failure")
return resp return resp
print('Starting client on ' + self.host + ':' + str(bindPort)) print('Starting client on ' + self.host + ':' + str(bindPort))
print('Client token:', clientToken) print('Client token:', self.clientToken)
app.run(host=self.host, port=bindPort, debug=True, threaded=True) app.run(host=self.host, port=bindPort, debug=True, threaded=True)
@ -68,10 +91,14 @@ class API:
if self.debug: if self.debug:
return return
# Validate host header, to protect against DNS rebinding attacks # Validate host header, to protect against DNS rebinding attacks
if request.host != '127.0.0.1:' + str(self.config['CLIENT']['PORT']): host = self.host
sys.exit(1) if not request.host.startswith('127'):
abort(403)
# Validate x-requested-with, to protect against CSRF/metadata leaks # Validate x-requested-with, to protect against CSRF/metadata leaks
'''
try: try:
request.headers['x-requested-with'] request.headers['x-requested-with']
except: except:
# we exit rather than abort to avoid fingerprinting
sys.exit(1) sys.exit(1)
'''

47
core.py Normal file
View File

@ -0,0 +1,47 @@
'''
Onionr - P2P Microblogging Platform & Social network
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sqlite3, os, time, math
class Core:
def __init__(self):
self.queueDB = 'data/queue.db'
self.daemonQueue() # Call to create the DB if it doesn't exist
return
def daemonQueue(self):
# Queue to exchange data between "client" and server.
retData = False
conn = sqlite3.connect(self.queueDB)
c = conn.cursor()
if not os.path.exists(self.queueDB):
# Create table
c.execute('''CREATE TABLE commands
(id integer primary key autoincrement, command text, data text, date text)''')
conn.commit()
else:
retData = c.execute('SELECT command, data, date, min(ID) FROM commands group by id')[0]
conn.close()
return retData
def daemonQueueAdd(self, command, data=''):
date = math.floor(time.time())
conn = sqlite3.connect(self.queueDB)
c = conn.cursor()
t = (command, data, date)
c.execute('INSERT into commands (command, data, date) values (?, ?, ?)', t)
conn.commit()
conn.close()
return

View File

@ -20,11 +20,12 @@ from colors import Colors
class Onionr: class Onionr:
def __init__(self): def __init__(self):
colors = Colors() colors = Colors()
# Get configuration and Handle commands # Get configuration and Handle commands
self.debug = True # Whole application debugging self.debug = False # Whole application debugging
os.chdir(sys.path[0]) os.chdir(sys.path[0])

28
tests.py Executable file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env python3
'''
Onionr - P2P Microblogging Platform & Social network
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import unittest, sys, os
class OnionrTests(unittest.TestCase):
def testNone(self):
# Test just running ./onionr with no arguments
blank = os.system('./onionr.py')
if blank != 0:
self.assertTrue(False)
else:
self.assertTrue(True)
unittest.main()