Merge remote-tracking branch 'origin/master' into erp5-component
[slapos.git] / stack / monitor / webfile-directory / index.cgi.in
1 #!{{ extra_eggs_interpreter }}
2
3 import cgi
4 import cgitb
5 import Cookie
6 import base64
7 import hashlib
8 import hmac
9 import jinja2
10 import os
11 import subprocess
12 import urllib
13
14 cgitb.enable(display=0, logdir="/tmp/cgi.log")
15
16 form = cgi.FieldStorage()
17 cookie = Cookie.SimpleCookie()
18
19 cgi_path = "{{ cgi_directory }}"
20
21 monitor_password_path = "{{ monitor_password_path }}"
22 monitor_password_script_path = "{{ monitor_password_script_path }}"
23
24 monitor_apache_password_command = "{{ apache_update_command }}"
25
26 ########
27 # Password functions
28 #######
29 def crypt(word, salt="$$"):
30   salt = salt.split("$")
31   algo = salt[0] or 'sha1'
32   if algo in hashlib.algorithms:
33     H = getattr(hashlib, algo)
34   elif algo == "plain":
35     return "%s$%s" % (algo, word)
36   else:
37     raise ValueError
38   rounds = min(max(0, int(salt[1])), 30) if salt[1] else 9
39   salt = salt[2] or base64.b64encode(os.urandom(12), "./")
40   h = hmac.new(salt, word, H).digest()
41   for x in xrange(1, 1 << rounds):
42     h = H(h).digest()
43   return "%s$%s$%s$%s" % (algo, rounds, salt,
44     base64.b64encode(h, "./").rstrip("="))
45
46 def is_password_set():
47   if not os.path.exists(monitor_password_path):
48     return False
49   hashed_password = open(monitor_password_path, 'r').read()
50   try:
51     void, algo, salt, hsh = hashed_password.split('$')
52   except ValueError:
53     return False
54   return True
55
56 def set_password(raw_password):
57   hashed_password = crypt(raw_password)
58   subprocess.check_call(monitor_apache_password_command + " %s" % raw_password,
59                         shell=True)
60   open(monitor_password_path, 'w').write(hashed_password)
61
62
63 def check_password(raw_password):
64   """
65   Returns a boolean of whether the raw_password was correct. Handles
66   encryption formats behind the scenes.
67   """
68   if not os.path.exists(monitor_password_path) or not raw_password:
69     return False
70   hashed_password = open(monitor_password_path, 'r').read()
71   return hashed_password == crypt(raw_password, hashed_password)
72 ### End of password functions
73
74 def forward_form():
75   command = os.path.join(cgi_path, form['posting-script'].value)
76   params_dict = {}
77   for f in form:
78     params_dict[f] = form[f].value
79   del params_dict['posting-script']
80   os.environ['QUERY_STRING'] = urllib.urlencode(params_dict)
81   try:
82     if os.access(command, os.X_OK):
83       print '\n', subprocess.check_output([command])
84   except subprocess.CalledProcessError:
85     print "There is a problem with sub-process"
86     pass
87
88
89 def return_document(command=None):
90   if not command:
91     script = form['script'].value
92     command = os.path.join(cgi_path, script)
93   #XXX this functions should be called only for display,
94   #so a priori it doesn't need form data
95   os.environ['QUERY_STRING'] = ''
96   try:
97     if os.access(command, os.X_OK):
98       print '\n', subprocess.check_output([command])
99     elif os.access(command, os.R_OK):
100       print open(command).read()
101     else:
102       raise OSError
103   except (subprocess.CalledProcessError, OSError) as e:
104     print "<p>Error :</p><pre>%s</pre>" % e
105
106
107 def make_menu():
108   # Transform deep-2 tree in json
109   folder_list = {}
110   for folder in os.listdir(cgi_path):
111     if os.path.isdir(os.path.join(cgi_path, folder)):
112       folder_list[folder] = []
113   for folder in folder_list:
114     for file in os.listdir(os.path.join(cgi_path, folder)):
115       if os.path.isfile(os.path.join(cgi_path, folder, file)):
116         folder_list[folder].append(file)
117   return folder_list
118
119
120 def get_cookie_password():
121   cookie_string = os.environ.get('HTTP_COOKIE')
122   if cookie_string:
123     cookie.load(cookie_string)
124     try:
125       return cookie['password'].value
126     except KeyError:
127       pass
128   return None
129
130 def set_cookie_password(password):
131   cookie['password'] = password
132   print cookie, "; Path=/; HttpOnly"
133
134
135 # Beginning of response
136 print "Content-Type: text/html"
137
138 password = None
139
140 # Check if user is logged
141 if "password_2" in form and "password" in form:
142   password_2 = form['password_2'].value
143   password_1 = form['password'].value
144   password = get_cookie_password()
145   if not is_password_set() or check_password(password):
146     if password_2 == password_1:
147       password = password_1
148       set_password(password)
149       set_cookie_password(password)
150 elif "password" in form:
151   password = form['password'].value
152   if is_password_set() and check_password(password):
153     set_cookie_password(password)
154 else:
155   password = get_cookie_password()
156 print '\n'
157
158
159 if not is_password_set():
160   return_document(monitor_password_script_path)
161 elif not check_password(password):
162   print "<html><head>"
163   print """
164     <link rel="stylesheet" href="static/pure-min.css">
165     <link rel="stylesheet" href="static/style.css">"""
166   print "</head><body>"
167   if password is None:
168     print "<h1>This is the monitoring interface</h1>"
169   else:
170     print "<h1>Error</h1><p>Wrong password</p>"
171   print """
172   <p>Please enter the monitor_password in the next field to access the data</p>
173   <form action="/index.cgi" method="post" class="pure-form-aligned">
174     Password : <input type="password" name="password">
175     <button type="submit" class="pure-button pure-button-primary">Access</button>
176   </form>
177   </body></html>"""
178 # redirection to the required script/page
179 else:
180   print
181   if "posting-script" in form:
182     forward_form()
183   elif "script" in form:
184     return_document()
185   else:
186     html_base = jinja2.Template(open('{{ index_template }}').read())
187     print
188     print html_base.render(tree=make_menu(), default_page="{{ default_page }}")