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
filename = settings.cgi.in
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]
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/${:filename}
......
......@@ -18,6 +18,7 @@ url = https://[$${slap-parameters:ipv6-random}]:$${:port}
index-filename = index.cgi
index-path = $${monitor-directory:www}/$${:index-filename}
db-path = $${monitor-directory:etc}/monitor.db
monitor-password-path = $${monitor-directory:etc}/.monitor.shadow
[monitor-directory]
recipe = slapos.cookbook:mkdirectory
......@@ -97,11 +98,14 @@ mode = 0644
recipe = slapos.recipe.template:jinja2
template = ${index:location}/${index:filename}
rendered = $${monitor-parameters:index-path}
update-apache-access = ${apache:location}/bin/htpasswd -cb $${monitor-parameters:htaccess-file} admin
mode = 0744
context =
key cgi_directory monitor-directory:cgi-bin
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 default_page /welcome.html
......@@ -139,6 +143,17 @@ context =
key pwd monitor-directory:knowledge0-cgi
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]
recipe = slapos.recipe.template:jinja2
template = ${monitor-bin:location}/${monitor-bin:filename}
......@@ -159,12 +174,6 @@ context =
section directory monitor-directory
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]
recipe = plone.recipe.command
command = ln -s $${:source} $${monitor-directory:private-directory}
......@@ -211,7 +220,6 @@ name = example.com
[public]
recipe = slapos.cookbook:zero-knowledge.write
filename = knowledge0.cfg
monitor-password = passwordtochange
[zero-parameters]
recipe = slapos.cookbook:zero-knowledge.read
......@@ -279,7 +287,7 @@ input = inline:
</Files>
AuthType Basic
AuthName "Private access"
AuthUserFile "$${monitor-htaccess:htaccess-path}"
AuthUserFile "$${monitor-parameters:htaccess-file}"
Require valid-user
Options Indexes FollowSymLinks
Satisfy all
......@@ -315,4 +323,3 @@ curl_path = ${curl:location}/bin/curl
[publish-connection-informations]
recipe = slapos.cookbook:publish
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 @@
import cgi
import cgitb
import Cookie
import base64
import hashlib
import hmac
import jinja2
import json
import os
import subprocess
import sys
import urllib
cgitb.enable(display=0, logdir="/tmp/cgi.log")
......@@ -17,6 +18,58 @@ cookie = Cookie.SimpleCookie()
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():
command = os.path.join(cgi_path, form['posting-script'].value)
......@@ -33,8 +86,10 @@ def forward_form():
pass
def return_document():
command = os.path.join(cgi_path, form['script'].value)
def return_document(command=None):
if not command:
script = form['script'].value
command = os.path.join(cgi_path, script)
#XXX this functions should be called only for display,
#so a priori it doesn't need form data
os.environ['QUERY_STRING'] = ''
......@@ -45,8 +100,8 @@ def return_document():
print open(command).read()
else:
raise OSError
except (subprocess.CalledProcessError, OSError):
print "<p>File cannot be found</p>"
except (subprocess.CalledProcessError, OSError) as e:
print "<p>Error :</p><pre>%s</pre>" % e
def make_menu():
......@@ -62,29 +117,48 @@ def make_menu():
return folder_list
# Beginning of response
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:
def get_cookie_password():
cookie_string = os.environ.get('HTTP_COOKIE')
if cookie_string:
cookie.load(cookie_string)
try:
password = cookie['password'].value
return cookie['password'].value
except KeyError:
password = None
else:
password = None
pass
return 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'
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 """
<link rel="stylesheet" href="pure-min.css">
......@@ -101,7 +175,6 @@ if not password or password != '{{ password }}':
<button type="submit" class="pure-button pure-button-primary">Access</button>
</form>
</body></html>"""
# redirection to the required script/page
else:
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