Added optional authentication and insert-highlighting

This commit is contained in:
Daniel 2017-01-29 22:11:33 +00:00
parent 76d9f78b84
commit 68948d4f09
5 changed files with 133 additions and 14 deletions

View file

@ -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.

View file

@ -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

View file

@ -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("""<!DOCTYPE html>
<div id="menu">
<div id="tree"></div><br />
<label for="triggers">Trigger platforms:</label><br />
<select id="triggers" onchange="editor.session.insert(editor.getCursorPosition(), this.value)">
<select id="triggers" onchange="insert(this.value)">
<option value="" disabled selected>...</option>
<option value="event">Event</option>
<option value="mqtt">MQTT</option>
@ -121,11 +124,11 @@ INDEX = Template("""<!DOCTYPE html>
<option value="zone">Zone</option>
</select><br /><br />
<label for="events">Events:</label><br />
<select id="events" onchange="editor.session.insert(editor.getCursorPosition(), this.value)"></select><br /><br />
<select id="events" onchange="insert(this.value)"></select><br /><br />
<label for="entities">Entities:</label><br />
<select id="entities" onchange="editor.session.insert(editor.getCursorPosition(), this.value)"></select><br /><br />
<select id="entities" onchange="insert(this.value)"></select><br /><br />
<label for="conditions">Conditions:</label><br />
<select id="conditions" onchange="editor.session.insert(editor.getCursorPosition(), this.value)">
<select id="conditions" onchange="insert(this.value)">
<option value="" disabled selected>...</option>
<option value="numeric_state">Numeric state</option>
<option value="state">State</option>
@ -135,7 +138,7 @@ INDEX = Template("""<!DOCTYPE html>
<option value="zone">Zone</option>
</select><br /><br />
<label for="services">Services:</label><br />
<select id="services" onchange="editor.session.insert(editor.getCursorPosition(), this.value)"></select>
<select id="services" onchange="insert(this.value)"></select>
</div>
<div id="toolbar">
<button id="savebutton" type="button" onclick="save()">Save</button>
@ -298,6 +301,11 @@ INDEX = Template("""<!DOCTYPE html>
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});
}
</script>
</html>
""")
@ -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)

View file

@ -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)

View file

@ -78,7 +78,7 @@
<div id="menu">
<div id="tree"></div><br />
<label for="triggers">Trigger platforms:</label><br />
<select id="triggers" onchange="editor.session.insert(editor.getCursorPosition(), this.value)">
<select id="triggers" onchange="insert(this.value)">
<option value="" disabled selected>...</option>
<option value="event">Event</option>
<option value="mqtt">MQTT</option>
@ -90,11 +90,11 @@
<option value="zone">Zone</option>
</select><br /><br />
<label for="events">Events:</label><br />
<select id="events" onchange="editor.session.insert(editor.getCursorPosition(), this.value)"></select><br /><br />
<select id="events" onchange="insert(this.value)"></select><br /><br />
<label for="entities">Entities:</label><br />
<select id="entities" onchange="editor.session.insert(editor.getCursorPosition(), this.value)"></select><br /><br />
<select id="entities" onchange="insert(this.value)"></select><br /><br />
<label for="conditions">Conditions:</label><br />
<select id="conditions" onchange="editor.session.insert(editor.getCursorPosition(), this.value)">
<select id="conditions" onchange="insert(this.value)">
<option value="" disabled selected>...</option>
<option value="numeric_state">Numeric state</option>
<option value="state">State</option>
@ -104,7 +104,7 @@
<option value="zone">Zone</option>
</select><br /><br />
<label for="services">Services:</label><br />
<select id="services" onchange="editor.session.insert(editor.getCursorPosition(), this.value)"></select>
<select id="services" onchange="insert(this.value)"></select>
</div>
<div id="toolbar">
<button id="savebutton" type="button" onclick="save()">Save</button>
@ -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});
}
</script>
</html>