Newer
Older
esp8266-door / daemon.py
@zhang zhang on 26 Oct 2018 4 KB auto-reconnect;
import paho.mqtt.client as mqtt
import ldap
import traceback
from peewee import *
from db_models import *
import config

class AuthStatus:
    SUCCESS       = 0
    LDAP_ERR      = -1
    NO_LDAP_ENTRY = -2
    NO_SUCH_USER  = -3
    ACCESS_DEINED = -4
    USER_DISABLED = -5
    EXCEPTION     = -1000

ADS_UF_ACCOUNTDISABLE=2 #https://docs.microsoft.com/en-us/windows/desktop/adschema/a-useraccountcontrol

client=mqtt.Client()

def verify_with_ldap(studnum):
    passed = False
    status = AuthStatus.LDAP_ERR
    conn = ldap.initialize(config.LDAP_URI)
    conn.protocol_version = 3
    conn.set_option(ldap.OPT_REFERRALS, 0)
    conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
    try:
        bind_result = conn.simple_bind_s(config.LDAP_USER, config.LDAP_PASS)
        # print("LDAP: Succesfully authenticated")
        search_results = conn.search_s(
                'DC=ad,DC=thu-skyworks,DC=org',
                ldap.SCOPE_SUBTREE,
                "(&(objectClass=person)(employeeNumber={}))".format(studnum),
            )
        search_results = filter(lambda s: s[0] is not None, search_results)
        cnt = 0;
        for dn,details in search_results:
            # print(details)
            if 'userAccountControl' in details: # MS AD
                print("userAccountControl =",int(details['userAccountControl'][0]))
                if int(details['userAccountControl'][0]) & ADS_UF_ACCOUNTDISABLE:
                    status = AuthStatus.USER_DISABLED
                    break
            cnt += 1
        else:
            if cnt>1:
                print("Warning: more than one user matched {}".format(studnum))
            if cnt>0:
                passed = True
                status = AuthStatus.SUCCESS
            else:
                status = AuthStatus.NO_SUCH_USER
    except ldap.INVALID_CREDENTIALS:
        print("LDAP: Invalid credentials")
    except ldap.SERVER_DOWN:
        print("LDAP: Server down")
    except ldap.LDAPError as e:
        print("LDAP: Other LDAP error: ", e)
    finally:
        conn.unbind_s()
    return passed, status

def log_handle(msg):
    print("[client]" + str(msg.payload))

def verify_card(msg):
    DB_connection_check()
    card_number = '{:010d}'.format(int(msg.payload))
    status = AuthStatus.EXCEPTION
    try:
        one = AccountInfo.select().where(AccountInfo.cardnum == card_number).get()

        print("Access Request by", one.studnum, one.realname)
        assert len(one.studnum) > 0 and int(one.studnum) > 0

        success, status = verify_with_ldap(one.studnum)
        if success:
            client.publish("/command", "open")
            print("Valid card, opening the door")
        else:
            print("Invalid card:", card_number)
        AccessRecords.create(
            realname = one.realname,
            studnum  = one.studnum,
            cardnum  = one.cardnum,
            status   = status,
            ).save()

    except AccountInfo.DoesNotExist:
        print("No Records Found for {}".format(msg.payload))
        AccessRecords.create(
            cardnum  = card_number,
            status   = AuthStatus.NO_SUCH_USER,
            ).save()
    except Exception as e:

        AccessRecords.create(
            cardnum  = card_number,
            status   = AuthStatus.EXCEPTION,
            ).save()

        raise e


topics = {
    "/log": log_handle,
    #"/rs485": log_handle,
    "/cardverify": verify_card}

def on_connect(client, userdata, flags, rc):
    print("Connected to MQTT broker with result code " + str(rc))
    client.subscribe("/cardverify")
    client.subscribe("/rs485")
    client.subscribe("/log")

def on_message(client, userdata, msg):
    # print("Received message " + str(msg.payload) + " from topic " + msg.topic)
    try:
        if msg.topic in topics:
            topics.get(msg.topic)(msg)
    except Exception as e:
        traceback.print_exc()

DB_Init()

client.on_connect = on_connect
client.on_message = on_message

client.username_pw_set(config.MQTT_USER, config.MQTT_PASSWORD)
client.connect(config.MQTT_BROKER, 1883)

client.loop_forever()