From eeafa6350fbcf56c9c0042a76a30d9be0149011c Mon Sep 17 00:00:00 2001 From: Ralf Ramsauer Date: Tue, 13 Mar 2018 18:26:12 +0100 Subject: [PATCH] Reimplement doorlockd in python Signed-off-by: Ralf Ramsauer --- doorlockd-new/doorlockd.py | 280 +++++++++++++++++++++++ doorlockd-new/static/led-green.png | Bin 0 -> 12687 bytes doorlockd-new/static/led-red.png | Bin 0 -> 12138 bytes doorlockd-new/static/logo.svg | 81 +++++++ doorlockd-new/templates/display.html | 25 ++ doorlockd-new/templates/formhelpers.html | 12 + doorlockd-new/templates/index.html | 55 +++++ doorlockd-new/templates/layout.html | 11 + 8 files changed, 464 insertions(+) create mode 100755 doorlockd-new/doorlockd.py create mode 100644 doorlockd-new/static/led-green.png create mode 100644 doorlockd-new/static/led-red.png create mode 100644 doorlockd-new/static/logo.svg create mode 100644 doorlockd-new/templates/display.html create mode 100644 doorlockd-new/templates/formhelpers.html create mode 100644 doorlockd-new/templates/index.html create mode 100644 doorlockd-new/templates/layout.html diff --git a/doorlockd-new/doorlockd.py b/doorlockd-new/doorlockd.py new file mode 100755 index 0000000..d1e1591 --- /dev/null +++ b/doorlockd-new/doorlockd.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 + +import argparse +import logging +import sys + +from enum import Enum +from threading import Thread +from time import sleep +from random import sample +from serial import Serial + +from flask import Flask, render_template, request, Markup +from flask_bootstrap import Bootstrap +from flask_socketio import SocketIO +from flask_wtf import FlaskForm + +from wtforms import PasswordField, StringField, SubmitField +from wtforms.validators import DataRequired, Length + +__author__ = 'Ralf Ramsauer' +__copyright = 'Copyright (c) Ralf Ramsauer, 2018' +__license__ = 'GPLv2' +__email__ = 'ralf@binary-kitchen.de' +__status__ = 'Development' +__maintainer__ = 'Ralf Ramsauer' +__version__ = '0.01a' + +log_level = logging.DEBUG +date_fmt = '%Y-%m-%d %H:%M:%S' +log_fmt = '%(asctime)-15s %(levelname)-8s %(message)s' +log = logging.getLogger() + +default_serial = '/dev/ttyS0' +default_ldap_uri = 'ldaps://ldap1.binary.kitchen/ ' \ + 'ldaps://ldap2.binary.kitchen/ ' \ + 'ldaps://ldapm.binary.kitchen/' +default_binddn = 'cn=%s,ou=people,dc=binary-kitchen,dc=de' + +html_title = 'Binary Kitchen Doorlock (%s - v%s)' % (__status__, __version__) + +webapp = Flask(__name__) +webapp.config['SECRET_KEY'] = 'foobar' +socketio = SocketIO(webapp, async_mode=None) +Bootstrap(webapp) + +# copied from sudo +eperm_insults = { + 'Wrong! You cheating scum!', + 'And you call yourself a Rocket Scientist!', + 'No soap, honkie-lips.', + 'Where did you learn to type?', + 'Are you on drugs?', + 'My pet ferret can type better than you!', + 'You type like i drive.', + 'Do you think like you type?', + 'Your mind just hasn\'t been the same since the electro-shock, has it?', + 'Maybe if you used more than just two fingers...', + 'BOB says: You seem to have forgotten your passwd, enter another!', + 'stty: unknown mode: doofus', + 'I can\'t hear you -- I\'m using the scrambler.', + 'The more you drive -- the dumber you get.', + 'Listen, broccoli brains, I don\'t have time to listen to this trash.', + 'I\'ve seen penguins that can type better than that.', + 'Have you considered trying to match wits with a rutabaga?', + 'You speak an infinite deal of nothing', +} + + +def choose_insult(): + return(sample(eperm_insults, 1)[0]) + + +class AuthMethod(Enum): + LDAP_USER_PW = 1 + + +class DoorState(Enum): + Close = 1 + Open = 2 + + def to_img(self): + led = 'red' + if self == DoorState.Open: + led = 'green' + return '' % led + + def to_html(self): + if self == DoorState.Open: + return 'Offen' + return 'Zu' + + +class LogicResponse(Enum): + Success = 0 + Perm = 1 + AlreadyLocked = 2 + AlreadyOpen = 3 + Inval = 4 + LDAP = 5 + + def to_html(self): + if self == LogicResponse.Success: + return 'Yo, passt.' + elif self == LogicResponse.Perm: + return choose_insult() + elif self == LogicResponse.AlreadyLocked: + return 'Narf. Schon zu.' + elif self == LogicResponse.AlreadyOpen: + return 'Schon offen, treten Sie ein!' + elif self == LogicResponse.Inval: + return 'Das was du willst geht nicht.' + elif self == LogicResponse.LDAP: + return 'Moep! Geh LDAP fixen!' + + return 'Bitte spezifizieren Sie.' + + +class DoorHandler: + state = DoorState.Close + + def __init__(self, device): + self.serial = Serial(device, baudrate=9600, bytesize=8, parity='N', + stopbits=1, timeout=1) + self.thread = Thread(target=self.thread_worker) + self.thread.start() + + def send_command(self, cmd): + print('sending cmd %c' % cmd) + + def clear_buffer(self): + print('clearing buffer...') + + def thread_worker(self): + while True: + self.clear_buffer() + + if self.state == DoorState.Open: + self.send_command('u') + elif self.state == DoorState.Close: + self.send_command('l') + sleep(1) + + def open(self): + if self.state == DoorState.Open: + return LogicResponse.AlreadyOpen + + self.state = DoorState.Open + return LogicResponse.Success + + def close(self): + if self.state == DoorState.Close: + return LogicResponse.AlreadyLocked + + self.state = DoorState.Close + return LogicResponse.Success + + def request(self, state): + if state == DoorState.Close: + return self.close() + elif state == DoorState.Open: + return self.open() + + +class Logic: + def __init__(self, device): + self.door_handler = DoorHandler(device) + + def _try_auth_ldap(self, user, password): + log.info('Trying to LDAP auth (user, password) as user %s', user) + return LogicResponse.Success + return LogicResponse.LDAP + + def try_auth(self, credentials): + method = credentials[0] + if method == AuthMethod.LDAP_USER_PW: + return self._try_auth_ldap(credentials[1], credentials[2]) + + return LogicResponse.Inval + + def _request(self, state, credentials): + err = self.try_auth(credentials) + if err != LogicResponse.Success: + return err + return self.door_handler.request(state) + + def request(self, state, credentials): + err = self._request(state, credentials) + return err + + @property + def state(self): + return self.door_handler.state + + +class AuthenticationForm(FlaskForm): + username = StringField('Username', [Length(min=4, max=25)]) + password = PasswordField('Password', [DataRequired()]) + open = SubmitField('Open') + close = SubmitField('Close') + + def __init__(self, *args, **kwargs): + FlaskForm.__init__(self, *args, **kwargs) + self.desired_state = DoorState.Close + + def validate(self): + if not FlaskForm.validate(self): + return False + + if self.open.data: + self.desired_state = DoorState.Open + + return True + + +def emit_status(message=None): + led = logic.state.to_img() + if message is None: + message = logic.state.to_html() + else: + message = message.to_html() + + socketio.emit('status', {'led': led, 'message': message}) + + +@socketio.on('connect') +def on_connect(): + emit_status() + + +@webapp.route('/display') +def display(): + return render_template('display.html') + + +@webapp.route('/', methods=['GET', 'POST']) +def home(): + authentication_form = AuthenticationForm() + response = None + + if request.method == 'POST' and authentication_form.validate(): + user = authentication_form.username.data + password = authentication_form.password.data + credentials = AuthMethod.LDAP_USER_PW, user, password + + log.info('Incoming request from %s' % user) + desired_state = authentication_form.desired_state + log.info(' desired state: %s' % desired_state) + log.info(' current state: %s' % logic.state) + response = logic.request(desired_state, credentials) + log.info(' response: %s' % response) + + # Don't trust python, zero credentials + user = password = credentials = None + + emit_status(response) + + return render_template('index.html', + authentication_form=authentication_form, + response=response, + state_text=logic.state.to_html(), + state_img=Markup(logic.state.to_img()), + title=html_title) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser('doorlockd', 'Binary Kitchen doorlockd') + parser.add_argument('-s', '--serial', default=default_serial, type=str) + args = parser.parse_args() + + logging.basicConfig(level=log_level, stream=sys.stdout, + format=log_fmt, datefmt=date_fmt) + log.info('Starting doorlockd') + log.info('Using serial port: %s' % args.serial) + + logic = Logic(args.serial) + + socketio.run(webapp, port=8080) + + sys.exit(0) diff --git a/doorlockd-new/static/led-green.png b/doorlockd-new/static/led-green.png new file mode 100644 index 0000000000000000000000000000000000000000..34e00809ffeca07b82e87bd3dc750da038b13f7f GIT binary patch literal 12687 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 literal 0 HcmV?d00001 diff --git a/doorlockd-new/static/led-red.png b/doorlockd-new/static/led-red.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6bb6c2dfbdb53c1f642a4fe71af950a21097b9 GIT binary patch literal 12138 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 + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/doorlockd-new/templates/display.html b/doorlockd-new/templates/display.html new file mode 100644 index 0000000..30479ea --- /dev/null +++ b/doorlockd-new/templates/display.html @@ -0,0 +1,25 @@ +{% extends "layout.html" %} + +{% block scripts %} + {{ super() }} + + +{% endblock %} + +{% block content %} +

Willkommen in der Binary Kitchen!

+
+
+{{ super() }} +{% endblock %} diff --git a/doorlockd-new/templates/formhelpers.html b/doorlockd-new/templates/formhelpers.html new file mode 100644 index 0000000..0b6bfda --- /dev/null +++ b/doorlockd-new/templates/formhelpers.html @@ -0,0 +1,12 @@ +{% macro render_field(field) %} +
{{ field.label }} +
{{ field(**kwargs)|safe }} + {% if field.errors %} +
    + {% for error in field.errors %} +
  • {{ error }}
  • + {% endfor %} +
+ {% endif %} +
+{% endmacro %} diff --git a/doorlockd-new/templates/index.html b/doorlockd-new/templates/index.html new file mode 100644 index 0000000..5b5a805 --- /dev/null +++ b/doorlockd-new/templates/index.html @@ -0,0 +1,55 @@ +{% from "formhelpers.html" import render_field %} + + + +{{ title }} + + + + + + +
+ {{ render_field(authentication_form.username) }} + {{ render_field(authentication_form.password) }} + {{ render_field(authentication_form.open) }} + {{ render_field(authentication_form.close) }} + {{ authentication_form.csrf_token }} +
+ {% if response %} +
+

{{ response.to_html() }}

+ {% endif %} +
+ Die Kitchen ist: {{ state_text }} + {{ state_img }} + + diff --git a/doorlockd-new/templates/layout.html b/doorlockd-new/templates/layout.html new file mode 100644 index 0000000..daa64d3 --- /dev/null +++ b/doorlockd-new/templates/layout.html @@ -0,0 +1,11 @@ +{%extends "bootstrap/base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{%- block content %} +{{super()}} +{%- block footer %} +


+ +{%- endblock footer %} +{%- endblock content %}