Commit 7e4ec422 authored by Cédric Le Ninivin's avatar Cédric Le Ninivin

monitor: Change password process to be stored individually

No more default password, it is to be set on first connection
Thanks Julien Muchembled for providing crypt function
parent e1d2fba2
...@@ -90,6 +90,15 @@ destination = ${buildout:directory}/parts/monitor-template-settings-cgi ...@@ -90,6 +90,15 @@ destination = ${buildout:directory}/parts/monitor-template-settings-cgi
filename = settings.cgi.in filename = settings.cgi.in
mode = 0644 mode = 0644
[monitor-password-cgi]
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/webfiles/${:filename}
download-only = true
#md5sum = 18574b804da0c65d8670959f9e7c4774
destination = ${buildout:directory}/parts/monitor-template-monitor-password-cgi
filename = monitor-password.cgi.in
mode = 0644
[rss-bin] [rss-bin]
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/${:filename} url = ${:_profile_base_location_}/${:filename}
......
...@@ -18,6 +18,7 @@ url = https://[$${slap-parameters:ipv6-random}]:$${:port} ...@@ -18,6 +18,7 @@ url = https://[$${slap-parameters:ipv6-random}]:$${:port}
index-filename = index.cgi index-filename = index.cgi
index-path = $${monitor-directory:www}/$${:index-filename} index-path = $${monitor-directory:www}/$${:index-filename}
db-path = $${monitor-directory:etc}/monitor.db db-path = $${monitor-directory:etc}/monitor.db
monitor-password-path = $${monitor-directory:etc}/.monitor.shadow
[monitor-directory] [monitor-directory]
recipe = slapos.cookbook:mkdirectory recipe = slapos.cookbook:mkdirectory
...@@ -97,11 +98,14 @@ mode = 0644 ...@@ -97,11 +98,14 @@ mode = 0644
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
template = ${index:location}/${index:filename} template = ${index:location}/${index:filename}
rendered = $${monitor-parameters:index-path} rendered = $${monitor-parameters:index-path}
update-apache-access = ${apache:location}/bin/htpasswd -cb $${monitor-parameters:htaccess-file} admin
mode = 0744 mode = 0744
context = context =
key cgi_directory monitor-directory:cgi-bin key cgi_directory monitor-directory:cgi-bin
raw index_template $${deploy-index-template:location}/$${deploy-index-template:filename} raw index_template $${deploy-index-template:location}/$${deploy-index-template:filename}
key password zero-parameters:monitor-password key monitor_password_path monitor-parameters:monitor-password-path
key monitor_password_script_path deploy-monitor-password-cgi:rendered
key apache_update_command :update-apache-access
raw extra_eggs_interpreter ${buildout:directory}/bin/${extra-eggs:interpreter} raw extra_eggs_interpreter ${buildout:directory}/bin/${extra-eggs:interpreter}
raw default_page /welcome.html raw default_page /welcome.html
...@@ -139,6 +143,17 @@ context = ...@@ -139,6 +143,17 @@ context =
key pwd monitor-directory:knowledge0-cgi key pwd monitor-directory:knowledge0-cgi
key this_file :filename key this_file :filename
[deploy-monitor-password-cgi]
recipe = slapos.recipe.template:jinja2
template = ${monitor-password-cgi:location}/${monitor-password-cgi:filename}
rendered = $${monitor-directory:knowledge0-cgi}/$${:filename}
filename = monitor-password.cgi
mode = 0744
context =
raw python_executable ${buildout:executable}
key pwd monitor-directory:knowledge0-cgi
key this_file :filename
[deploy-monitor-script] [deploy-monitor-script]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
template = ${monitor-bin:location}/${monitor-bin:filename} template = ${monitor-bin:location}/${monitor-bin:filename}
...@@ -159,12 +174,6 @@ context = ...@@ -159,12 +174,6 @@ context =
section directory monitor-directory section directory monitor-directory
section monitor_parameters monitor-parameters section monitor_parameters monitor-parameters
[monitor-htaccess]
recipe = plone.recipe.command
stop-on-error = true
htaccess-path = $${monitor-parameters:htaccess-file}
command = ${apache:location}/bin/htpasswd -cb $${:htaccess-path} admin $${zero-parameters:monitor-password}
[monitor-directory-access] [monitor-directory-access]
recipe = plone.recipe.command recipe = plone.recipe.command
command = ln -s $${:source} $${monitor-directory:private-directory} command = ln -s $${:source} $${monitor-directory:private-directory}
...@@ -211,7 +220,6 @@ name = example.com ...@@ -211,7 +220,6 @@ name = example.com
[public] [public]
recipe = slapos.cookbook:zero-knowledge.write recipe = slapos.cookbook:zero-knowledge.write
filename = knowledge0.cfg filename = knowledge0.cfg
monitor-password = passwordtochange
[zero-parameters] [zero-parameters]
recipe = slapos.cookbook:zero-knowledge.read recipe = slapos.cookbook:zero-knowledge.read
...@@ -279,7 +287,7 @@ input = inline: ...@@ -279,7 +287,7 @@ input = inline:
</Files> </Files>
AuthType Basic AuthType Basic
AuthName "Private access" AuthName "Private access"
AuthUserFile "$${monitor-htaccess:htaccess-path}" AuthUserFile "$${monitor-parameters:htaccess-file}"
Require valid-user Require valid-user
Options Indexes FollowSymLinks Options Indexes FollowSymLinks
Satisfy all Satisfy all
...@@ -315,4 +323,3 @@ curl_path = ${curl:location}/bin/curl ...@@ -315,4 +323,3 @@ curl_path = ${curl:location}/bin/curl
[publish-connection-informations] [publish-connection-informations]
recipe = slapos.cookbook:publish recipe = slapos.cookbook:publish
monitor_url = $${monitor-parameters:url} monitor_url = $${monitor-parameters:url}
IMPORTANT_monitor_info = Change the monitor_password as soon as possible ! Default is : $${public:monitor-password} . You can change it in the setting.cgi section of your monitorin interface
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
import cgi import cgi
import cgitb import cgitb
import Cookie import Cookie
import base64
import hashlib
import hmac
import jinja2 import jinja2
import json
import os import os
import subprocess import subprocess
import sys
import urllib import urllib
cgitb.enable(display=0, logdir="/tmp/cgi.log") cgitb.enable(display=0, logdir="/tmp/cgi.log")
...@@ -17,6 +18,58 @@ cookie = Cookie.SimpleCookie() ...@@ -17,6 +18,58 @@ cookie = Cookie.SimpleCookie()
cgi_path = "{{ cgi_directory }}" cgi_path = "{{ cgi_directory }}"
monitor_password_path = "{{ monitor_password_path }}"
monitor_password_script_path = "{{ monitor_password_script_path }}"
monitor_apache_password_command = "{{ apache_update_command }}"
########
# Password functions
#######
def crypt(word, salt="$$"):
salt = salt.split("$")
algo = salt[0] or 'sha1'
if algo in hashlib.algorithms:
H = getattr(hashlib, algo)
elif algo == "plain":
return "%s$%s" % (algo, word)
else:
raise ValueError
rounds = min(max(0, int(salt[1])), 30) if salt[1] else 9
salt = salt[2] or base64.b64encode(os.urandom(12), "./")
h = hmac.new(salt, word, H).digest()
for x in xrange(1, 1 << rounds):
h = H(h).digest()
return "%s$%s$%s$%s" % (algo, rounds, salt,
base64.b64encode(h, "./").rstrip("="))
def is_password_set():
if not os.path.exists(monitor_password_path):
return False
hashed_password = open(monitor_password_path, 'r').read()
try:
void, algo, salt, hsh = hashed_password.split('$')
except ValueError:
return False
return True
def set_password(raw_password):
hashed_password = crypt(raw_password)
subprocess.check_call(monitor_apache_password_command + " %s" % raw_password,
shell=True)
open(monitor_password_path, 'w').write(hashed_password)
def check_password(raw_password):
"""
Returns a boolean of whether the raw_password was correct. Handles
encryption formats behind the scenes.
"""
if not os.path.exists(monitor_password_path) or not raw_password:
return False
hashed_password = open(monitor_password_path, 'r').read()
return hashed_password == crypt(raw_password, hashed_password)
### End of password functions
def forward_form(): def forward_form():
command = os.path.join(cgi_path, form['posting-script'].value) command = os.path.join(cgi_path, form['posting-script'].value)
...@@ -33,8 +86,10 @@ def forward_form(): ...@@ -33,8 +86,10 @@ def forward_form():
pass pass
def return_document(): def return_document(command=None):
command = os.path.join(cgi_path, form['script'].value) if not command:
script = form['script'].value
command = os.path.join(cgi_path, script)
#XXX this functions should be called only for display, #XXX this functions should be called only for display,
#so a priori it doesn't need form data #so a priori it doesn't need form data
os.environ['QUERY_STRING'] = '' os.environ['QUERY_STRING'] = ''
...@@ -45,8 +100,8 @@ def return_document(): ...@@ -45,8 +100,8 @@ def return_document():
print open(command).read() print open(command).read()
else: else:
raise OSError raise OSError
except (subprocess.CalledProcessError, OSError): except (subprocess.CalledProcessError, OSError) as e:
print "<p>File cannot be found</p>" print "<p>Error :</p><pre>%s</pre>" % e
def make_menu(): def make_menu():
...@@ -62,29 +117,48 @@ def make_menu(): ...@@ -62,29 +117,48 @@ def make_menu():
return folder_list return folder_list
# Beginning of response def get_cookie_password():
print "Content-Type: text/html"
# Check if user is logged
if "password" in form:
password = form['password'].value
if password == '{{ password }}' :
cookie['password'] = password
print cookie, "; Path=/; HttpOnly"
else:
cookie_string = os.environ.get('HTTP_COOKIE') cookie_string = os.environ.get('HTTP_COOKIE')
if cookie_string: if cookie_string:
cookie.load(cookie_string) cookie.load(cookie_string)
try: try:
password = cookie['password'].value return cookie['password'].value
except KeyError: except KeyError:
password = None pass
else: return None
password = None
def set_cookie_password(password):
cookie['password'] = password
print cookie, "; Path=/; HttpOnly"
# Beginning of response
print "Content-Type: text/html"
password = None
# Check if user is logged
if "password_2" in form and "password" in form:
password_2 = form['password_2'].value
password_1 = form['password'].value
password = get_cookie_password()
if not is_password_set() or check_password(password):
if password_2 == password_1:
password = password_1
set_password(password)
set_cookie_password(password)
elif "password" in form:
password = form['password'].value
if is_password_set() and check_password(password):
set_cookie_password(password)
else:
password = get_cookie_password()
print '\n' print '\n'
if not password or password != '{{ password }}':
if not is_password_set():
return_document(monitor_password_script_path)
elif not check_password(password):
print "<html><head>" print "<html><head>"
print """ print """
<link rel="stylesheet" href="pure-min.css"> <link rel="stylesheet" href="pure-min.css">
...@@ -101,7 +175,6 @@ if not password or password != '{{ password }}': ...@@ -101,7 +175,6 @@ if not password or password != '{{ password }}':
<button type="submit" class="pure-button pure-button-primary">Access</button> <button type="submit" class="pure-button pure-button-primary">Access</button>
</form> </form>
</body></html>""" </body></html>"""
# redirection to the required script/page # redirection to the required script/page
else: else:
print print
......
#!{{ python_executable }}
import cgitb
cgitb.enable()
print "<html><head>"
print """
<script type="text/javascript" src="/jquery-1.10.2.min.js"></script>
<link rel="stylesheet" href="pure-min.css">
<link rel="stylesheet" href="/style.css">"""
print "</head><body>"
print "<h1>This is the monitoring interface</h1>"
print "<h2>Please set your password for later access</h2>"
print """
<form action="/index.cgi" method="post" class="pure-form-aligned">
<div class="pure-control-group">
<label for="password">Password*:</label>
<input placeholder="Set your password" type="password" name="password" id="password"></br>
</div><div class="pure-control-group">
<label for="password">Verify Password*:</label>
<input placeholder="Verify password" type="password" name="password_2" id="password_2"></br>
</div><p id="validate-status" style="color:red"></p>
<div class="pure-controls">
<button id="register-button" type="submit" class="pure-button pure-button-primary" disabled>Access</button></div>
</form>
<script type="text/javascript" src="monitor-register.js"></script>
</body></html>
"""
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment