Project-wide refactor of things named Component* to EventHandler*

This commit is contained in:
Bálint Bokros
2017-12-02 15:19:05 +01:00
parent acaf8899cc
commit 62a12c605c
14 changed files with 32 additions and 34 deletions

View File

View File

@ -0,0 +1,18 @@
import json
from functools import partial
from event_handler_base import EventHandlerBase
class EventHandler(EventHandlerBase):
def __init__(self, anchor, event_handler_function, zmq_context=None):
super().__init__(anchor, event_handler_function, zmq_context)
def wrapper(msg_parts, handler):
anchor, message = msg_parts
data_json = json.loads(message)
response = handler(data_json, self)
encoded_response = json.dumps(response).encode('utf-8')
self.zmq_push_socket.send_multipart([anchor, encoded_response])
self.zmq_sub_stream.on_recv(partial(wrapper, handler=event_handler_function))

View File

@ -0,0 +1,48 @@
import json
import zmq
from zmq.eventloop import ioloop
from zmq.eventloop.zmqstream import ZMQStream
from config import PUBLISHER_PORT, RECEIVER_PORT
ioloop.install()
class EventHandlerBase:
def __init__(self, anchor, event_handler_function, zmq_context=None):
self.anchor = anchor
self.event_handler_function = event_handler_function
self.zmq_context = zmq_context or zmq.Context.instance()
self.zmq_sub_socket = self.zmq_context.socket(zmq.SUB)
self.subscriptions = {self.anchor}
self.zmq_sub_socket.setsockopt_string(zmq.SUBSCRIBE, self.anchor)
self.zmq_sub_socket.connect('tcp://localhost:{}'.format(PUBLISHER_PORT))
self.zmq_sub_stream = ZMQStream(self.zmq_sub_socket)
self.zmq_push_socket = self.zmq_context.socket(zmq.PUSH)
self.zmq_push_socket.connect('tcp://localhost:{}'.format(RECEIVER_PORT))
def message_other(self, anchor, data):
encoded_anchor = anchor.encode('utf-8')
message = {
'anchor': anchor,
'data': data
}
encoded_message = json.dumps(message).encode('utf-8')
self.zmq_push_socket.send_multipart([encoded_anchor, encoded_message])
def subscribe(self, anchor):
if anchor not in self.subscriptions:
self.subscriptions.add(anchor)
self.zmq_sub_socket.setsockopt_string(zmq.SUBSCRIBE, anchor)
def unsubscribe(self, anchor):
try:
self.subscriptions.remove(anchor)
self.zmq_sub_socket.setsockopt_string(zmq.UNSUBSCRIBE, anchor)
except KeyError:
pass
def unsubscribe_all(self):
for sub in self.subscriptions:
self.zmq_sub_socket.setsockopt_string(zmq.UNSUBSCRIBE, sub)
self.subscriptions.clear()

View File

@ -0,0 +1,86 @@
import codecs
import sqlite3
import source_code
from event_handler import EventHandler
from stateful_event_handler import StatefulEventHandler
from tornado.ioloop import IOLoop
from login_component import authorize_login
def echo_handler(data):
return data
def rot13_handler(data, *args):
data['data'] = codecs.encode(data['data'], 'rot13')
return data
def change_case_handler(data, *args):
data['data'] = data['data'].upper() if data['data'].islower() else data['data'].lower()
return data
def reverse_handler(data, *args):
data['data'] = data['data'][::-1]
return data
def login_handler(data, component):
email, password = data['data']['email'], data['data']['password']
try:
sql_statement = source_code.find_local_variable_value(authorize_login, 'sql_statement')
yield (
'anchor_logger',
'The SQL statement executed by the server will look like this:\n `{}`'.format(sql_statement)
)
yield ('anchor_webide',
source_code.get_source_code(authorize_login, strip_comments=False))
sql_statement_with_values = sql_statement.format(email, password)
yield (
'anchor_logger',
'After the submitted parameters are substituted it looks like this:\n `{}`'.format(
sql_statement_with_values
)
)
logged_in_email, is_admin = authorize_login(email, password)
yield (
'anchor_logger',
'After the query is executed, it returns _{}_ as email address, and _{}_ for is_admin'.format(
logged_in_email, is_admin
)
)
if logged_in_email is not None:
response = 'Logged in as _{}_. You __{}have__ admin privileges.'.format(
logged_in_email,
'' if is_admin else 'don\'t '
)
else:
response = 'Bad username/password!'
except sqlite3.Warning:
response = 'Invalid request!'
yield ('anchor_login', '# Login page\n' + response)
def source_code_handler(data, event_handler):
event_handler.unsubscribe(data['anchor'])
yield (data['anchor'],
source_code.get_source_code(authorize_login, strip_comments=True))
if __name__ == '__main__':
anchor_a = EventHandler('anchor_a', change_case_handler)
anchor_b = EventHandler('anchor_b', rot13_handler)
anchor_c = EventHandler('anchor_c', reverse_handler)
anchor_login = StatefulEventHandler('anchor_login', login_handler)
anchor_webide = StatefulEventHandler('anchor_webide', source_code_handler)
IOLoop.instance().start()

View File

@ -0,0 +1,27 @@
import sqlite3
def get_db():
return sqlite3.connect('users.db')
def authorize_login(email, password):
"""
This method checks if a user is authorized and has admin privileges.
:param email: The email address of the user.
:param password: The password of the user.
:return: A tuple, the first element is the email address if the user exists,
and None if they don't; the second element is a boolean, which is True if
the user has admin privileges.
"""
conn = get_db()
sql_statement = '''SELECT email, is_admin FROM users
WHERE email="{}" AND password="{}"'''
# The problem with this approach is that it substitutes any value received
# from the user, even if it is a valid SQL statement!
result = conn.execute(sql_statement.format(email, password)).fetchone()
if result is None:
return None, False
else:
email, is_admin = result
return email, is_admin == 1

View File

@ -0,0 +1,39 @@
import json
from functools import partial
import zmq
from event_handler_base import EventHandlerBase
class StatefulEventHandler(EventHandlerBase):
def __init__(self, anchor, event_handler, zmq_context=None):
super().__init__(anchor, event_handler, zmq_context)
self.generator = None
self.subscribe('reset')
def wrapper(msg_parts, handler):
anchor, message = msg_parts
if anchor == b'reset':
self.generator = None
self.unsubscribe_all()
self.subscribe(self.anchor)
self.subscribe('reset')
return
data_json = json.loads(message)
if self.generator is None:
self.generator = handler(data_json, self)
response_anchor, response_data = next(self.generator)
if response_anchor is None:
return
if response_anchor not in self.subscriptions:
self.subscriptions.add(response_anchor)
self.zmq_sub_socket.setsockopt_string(zmq.SUBSCRIBE, response_anchor)
response_data = json.dumps({
'anchor': response_anchor,
'data': response_data,
})
response = [r.encode('utf-8') for r in (response_anchor, response_data)]
self.zmq_push_socket.send_multipart(response)
self.zmq_sub_stream.on_recv(partial(wrapper, handler=event_handler))

BIN
src/event_handlers/users.db Normal file

Binary file not shown.