Create_and_Host_an_ENGAGE_Device_Server

Create and Host an ENGAGE Device Server

ENGAGE Device System Server Example

The example code shown herein can be executed to demonstrate a bare-bones implementation of an unsecured server.

For production purposes all servers should be secured via TLS. An example of a TLS server will be provided later.

To host an ENGAGE device server, it is first necessary to download and install specific software. Once that is completed, you will be directed on how to host a server. To obtain the Python and Flask software to be downloaded, go to the Download and Install Python and Flask Software page and follow the instructions.

Introduction to Creating and Hosting an ENGAGE Device Server

The example code is developed using Python scripting. These examples can be used to manage support for WiFi based Device control systems.

These examples are based on Flask. Flask is a BSD licensed micro web development framework written in Python based on Werkzeug and Jinja 2.

from flask \
    import Flask, request, render_template

from werkzeug.serving \
    import WSGIRequestHandler

Webbrowser is a high level web-based page presentation interface which is used in this example as a simple means of handling web pages.

import webbrowser
import requests
import base64

Flask configuration

The following is an example of types of configuration possible for this server, used in control and management of the server instance.

app = Flask(__name__)
app.config['DEBUG'] = True  # runs flask in debug mode
app.config['JSON_SORT_KEYS'] = False  # prevents json sorting
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False  # prevents JSON pretty print

The protocol version necessary for implementation of the ENGAGE site configuration is:

WSGIRequestHandler.protocol_version = "HTTP/1.1" which forces HTTP 1.1 header necessary for the lock.

Global definitions

  • lock_name_search = "{'name': "
  • config_lock_name = "Default SAM User"

Defined URL/API handlers

  • url1_site = "https://partner.lockwebserv.com/engage/sites"
  • url2_devs = "https://partner.lockwebserv.com/engage/devices/site/81684db6-c94f-475a-8681-fe29412ef9a3"
  • url3_sitesofware = "https://partner.lockwebserv.com/engage/lookup/sitesoftware"
  • url3_edgeDevices = "https://partner.lockwebserv.com/edgeDevices"
  • url2 = "https://portal.allegionengage.com/landing/nonengagemanaged"
  • url4_register = "https://partner.lockwebserv.com/engage/sites/register"
  • url5_site_defs = "https://partner.lockwebserv.com/engage/sites/81684db6-c94f-475a-8681-fe29412ef9a3/devicedefaults"

The username and password used in API processing that require administrative access to site and user functions. Replace this login info with valid credentials.

username = "john.dow@securitycompany.com"
password = "Pwd1234?"

str1 = (username + ':' + password).encode('utf-8')
auth = base64.b64encode(str1)
headers = {
    'Authorization': "Basic " + str(auth.decode('utf-8')),
    'Content-Type': "application/json"
}

Device Credential Site Key Example:

site_key = "06fba5258a37eed076532ec47bccc2f7daa92edf5490f722234eec6f5ac7c467"

Encrypted Device/User Credential Example:

This is derived from a physical card or fob and is downloaded to the device in the user database.

cred3 = '0d6807e6dbac61481a8ff8b0dce95998'

SiteReference = "SiteReference"

Door File Route

Route to handle the door file that is returned from the selected managed device and the credential file to be sent to the managed device.

In this example, the door file returned from the managed device is stored in a local flat file. This data could be captured in the site specific database manager.

The door file database file is returned from this route. This credential file was created in an earlier operation and contains the user and access method for each credential on that specific managed device.

In this case, the route returns the credential Database file to be downloaded to the managed device as well as the standard return status.

@app.route('/swordfish/lock/db/<path:timestamp>', methods=['GET'])
def get_door_file(timestamp):
    global config_lock_name
    f = open("./Engage_Data/Lock_Door_File.json", "w+")
    f.write(str(request.json))
    f.close()
    user1 = open("./Engage_Data/Engage_User_DB_Formatted.json", 'r')
    user1_data_out = user1.read()
    user1.close()
    print("**  Returned Door File from Managed Device   :", config_lock_name)
    return user1_data_out, 200

Managed Device Config File Route

Route to handle the Managed Device Config File.

The managed device config file is returned to the server and is written to a flat file. This file could be managed in the enterprise database manager.

The function find_lock_name is called to get the name of the Managed Device. This name is made global, and is made available to all the route handling routines.

Nothing is downloaded to the managed device, so no data is returned. This standard return status is also returned.

@app.route('/swordfish/lock/Config', methods=['PUT'])
def put_config():
    global config_lock_name
    config_json = request.json
    config_lock_name = find_lock_name(str(request.json))
    f = open("./Engage_Data/Lock_Config.json", "w+")
    f.write(str(config_json))
    f.close()
    print("**  Returned Config File from Managed Device :", config_lock_name)
    return '', 200

Managed Device Audit File

Route to handle the Managed Device Audit File.

The managed device Audit file is returned to the server and is written to a flat file. This file could be managed in the enterprise database manager.

The Managed Device was determined from the config file and is made available to this route handler. The Managed Device name is appended to the Audit file. Nothing is downloaded to the managed device, so no data is returned. The standard return status is also returned.

@app.route('/swordfish/lock/audit', methods=['POST'])
def post_audit():
    global config_lock_name
    f = open("./Engage_Data/Lock_Audit.json", "w+")
    f.write(config_lock_name)
    f.write(str(request.json))
    f.close()
    print("**  Returned Audit File from Managed Device  :", config_lock_name)
    return '', 202

Managed Device Alert File

Route to handle the Managed Device Alert File.

The managed device Alert file is returned to the server and is written to a flat file. This file could be managed in the enterprise database manager.

The Managed Device was determined from the config file and is made available to this route handler. The Managed Device name is appended to the Alert file. Nothing is downloaded to the managed device, so no data is returned. The standard return status is also returned.

@app.route('/swordfish/lock/alert', methods=['POST'])
def post_alert():
    global config_lock_name
    f = open("./Engage_Data/Lock_Alert.json", "w+")
    f.write(config_lock_name)
    f.write(str(request.json))
    f.close()
    print("**  Returned Alert File from Managed Device  :", config_lock_name)
    return '', 200

Managed Device Certificates

This route is used to handle non specific server connections from services other than specifically defined in this server.

@app.route('/', methods=['GET', 'POST'])
def handle_other_network():
    print("Request from outside")
    return "UnHandled Request", 404

SSL Encryption Security Handler.

@app.route('/swordfish/lock/certificates/current', methods=['GET'])
def sam_certificates():
    print("SAM certificates", request)
    print('Sending Certificate URL and hash to Client.')
    return '{"cert_url":"http://localhost/static/Certificates/cert.der","hash":"9e177a284426f7accc28b26b318b90ed"}'
    #return render_template('url2')

Start the web server.

This is an example Flask web server that is open to all network Addresses. No web address control is implemented. It is the responsibility of the user enterprise to implement web and server security.

The route handle_other_network(): returns an un-authorized message to connection attempts, but this is NOT an adequate security layer.

app.run(host='0.0.0.0', port=80)