From 68948d4f09fd47ddc346a72118b9a98a58de3fdc Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 29 Jan 2017 22:11:33 +0000 Subject: [PATCH] Added optional authentication and insert-highlighting --- README.md | 2 ++ changelog.txt | 4 +++ configurator.py | 69 +++++++++++++++++++++++++++++++++++++++++---- dev/configurator.py | 57 +++++++++++++++++++++++++++++++++++-- dev/index.html | 15 ++++++---- 5 files changed, 133 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b4f5dcc..0006674 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ If you're using SSL, set the paths to your SSL files here. This is similar to th The configurator fetches some data from your running HASS instance. If the API isn't available through the default URL, modify this variable to fix this. ####HASS_API_PASSWORD (string) If you plan on using the restart button, you have to set your API password. Calling the restart service of HASS is prohibited without authentication. +####CREDENTIALS (string) +Set credentials in the form of `"username:password"` if authentication should be required for access. ###Embedding into HASS HASS has the [panel_iframe](https://home-assistant.io/components/panel_iframe/) component. With this it is possible to embed the configurator directly into HASS, allowing you to modify your configuration through the HASS frontend. diff --git a/changelog.txt b/changelog.txt index a0279a9..18afe5b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +Version 0.0.7 (2017-01-nn) +- Added optional authentication +- Inserted elements are selected as visual feedback + Version 0.0.6 (2017-01-29) - Added SSL support - Added HASS restart button diff --git a/configurator.py b/configurator.py index 0f48b56..08c21c7 100755 --- a/configurator.py +++ b/configurator.py @@ -9,6 +9,7 @@ import json import ssl import socketserver import urllib.request +import base64 from string import Template from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse, parse_qs @@ -24,6 +25,8 @@ SSL_KEY = None # Set the destination where the HASS API is reachable HASS_API = "http://127.0.0.1:8123/api/" HASS_API_PASSWORD = None +# To enable authentication, set the credentials in the form of "username:password" +CREDENTIALS = None ### End of options RELEASEURL = "https://api.github.com/repos/danielperna84/hass-poc-configurator/releases/latest" @@ -109,7 +112,7 @@ INDEX = Template("""
@@ -298,6 +301,11 @@ INDEX = Template(""" editor.setOption("displayIndentGuides", true); editor.setOption("highlightSelectedWord", highlightwords); editor.$blockScrolling = Infinity; + function insert(text) { + var pos = editor.selection.getCursor(); + var end = editor.session.insert(pos, text); + editor.selection.setRange({start:pos, end:end}); + } """) @@ -455,11 +463,60 @@ class RequestHandler(BaseHTTPRequestHandler): self.wfile.write(bytes(response, "utf8")) return +class AuthHandler(RequestHandler): + def do_HEAD(self): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_AUTHHEAD(self): + print("Requesting authorization") + self.send_response(401) + self.send_header('WWW-Authenticate', 'Basic realm=\"HASS-PoC-Configurator\"') + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_GET(self): + global CREDENTIALS + authorization = self.headers.get('Authorization', None) + if authorization == None: + self.do_AUTHHEAD() + self.wfile.write(bytes('no auth header received', 'utf-8')) + pass + elif authorization == 'Basic %s' % CREDENTIALS.decode('utf-8'): + super().do_GET() + pass + else: + self.do_AUTHHEAD() + self.wfile.write(bytes('not authenticated', 'utf-8')) + pass + + def do_POST(self): + global CREDENTIALS + authorization = self.headers.get('Authorization', None) + if authorization == None: + self.do_AUTHHEAD() + self.wfile.write(bytes('no auth header received', 'utf-8')) + pass + elif authorization == 'Basic %s' % CREDENTIALS.decode('utf-8'): + super().do_POST() + pass + else: + self.do_AUTHHEAD() + self.wfile.write(bytes('not authenticated', 'utf-8')) + pass + def run(): + global CREDENTIALS print('Starting server') server_address = (LISTENIP, LISTENPORT) + if CREDENTIALS: + CREDENTIALS = base64.b64encode(bytes(CREDENTIALS, "utf-8")) + Handler = AuthHandler + else: + Handler = RequestHandler if not SSL_CERTIFICATE: - httpd = HTTPServer(server_address, RequestHandler) + httpd = HTTPServer(server_address, Handler) else: httpd = socketserver.TCPServer(server_address, RequestHandler) httpd.socket = ssl.wrap_socket(httpd.socket, certfile=SSL_CERTIFICATE, keyfile=SSL_KEY, server_side=True) diff --git a/dev/configurator.py b/dev/configurator.py index 28de1ba..98d615f 100755 --- a/dev/configurator.py +++ b/dev/configurator.py @@ -13,7 +13,7 @@ from string import Template from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse, parse_qs -### Some options for you to change +######### Some options for you to change ######### LISTENIP = "0.0.0.0" LISTENPORT = 3218 # Set BASEPATH to something like "/home/hass/.homeasssitant" if you're not running the configurator from that path @@ -23,7 +23,9 @@ SSL_CERTIFICATE = None SSL_KEY = None # Set the destination where the HASS API is reachable HASS_API = "http://127.0.0.1:8123/api/" -### End of options +# To enable authentication, set the credentials in the form of "username:password" +CREDENTIALS = None +################# End of options ################# RELEASEURL = "https://api.github.com/repos/danielperna84/hass-poc-configurator/releases/latest" VERSION = "0.0.6" @@ -165,11 +167,60 @@ class RequestHandler(BaseHTTPRequestHandler): self.wfile.write(bytes(response, "utf8")) return +class AuthHandler(RequestHandler): + def do_HEAD(self): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_AUTHHEAD(self): + print("Requesting authorization") + self.send_response(401) + self.send_header('WWW-Authenticate', 'Basic realm=\"HASS-PoC-Configurator\"') + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_GET(self): + global CREDENTIALS + authorization = self.headers.get('Authorization', None) + if authorization == None: + self.do_AUTHHEAD() + self.wfile.write(bytes('no auth header received', 'utf-8')) + pass + elif authorization == 'Basic %s' % CREDENTIALS.decode('utf-8'): + super().do_GET() + pass + else: + self.do_AUTHHEAD() + self.wfile.write(bytes('not authenticated', 'utf-8')) + pass + + def do_POST(self): + global CREDENTIALS + authorization = self.headers.get('Authorization', None) + if authorization == None: + self.do_AUTHHEAD() + self.wfile.write(bytes('no auth header received', 'utf-8')) + pass + elif authorization == 'Basic %s' % CREDENTIALS.decode('utf-8'): + super().do_POST() + pass + else: + self.do_AUTHHEAD() + self.wfile.write(bytes('not authenticated', 'utf-8')) + pass + def run(): + global CREDENTIALS print('Starting server') server_address = (LISTENIP, LISTENPORT) + if CREDENTIALS: + CREDENTIALS = base64.b64encode(bytes(CREDENTIALS, "utf-8")) + Handler = AuthHandler + else: + Handler = RequestHandler if not SSL_CERTIFICATE: - httpd = HTTPServer(server_address, RequestHandler) + httpd = HTTPServer(server_address, Handler) else: httpd = socketserver.TCPServer(server_address, RequestHandler) httpd.socket = ssl.wrap_socket(httpd.socket, certfile=SSL_CERTIFICATE, keyfile=SSL_KEY, server_side=True) diff --git a/dev/index.html b/dev/index.html index 35fffba..c32de48 100644 --- a/dev/index.html +++ b/dev/index.html @@ -78,7 +78,7 @@
@@ -267,5 +267,10 @@ editor.setOption("displayIndentGuides", true); editor.setOption("highlightSelectedWord", highlightwords); editor.$blockScrolling = Infinity; + function insert(text) { + var pos = editor.selection.getCursor(); + var end = editor.session.insert(pos, text); + editor.selection.setRange({start:pos, end:end}); + }