index.cgi.in 5.39 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
#!{{ extra_eggs_interpreter }}

import cgi
import cgitb
import Cookie
import base64
import hashlib
import hmac
import jinja2
import os
import subprocess
import urllib

cgitb.enable(display=0, logdir="/tmp/cgi.log")

form = cgi.FieldStorage()
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 }}"

monitor_rewrite = "{{ ' '.join(rewrite_element.keys())  }}"

########
# 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)
  params_dict = {}
  for f in form:
    params_dict[f] = form[f].value
  del params_dict['posting-script']
  os.environ['QUERY_STRING'] = urllib.urlencode(params_dict)
  try:
    if os.access(command, os.X_OK):
      print '\n', subprocess.check_output([command])
  except subprocess.CalledProcessError:
    print "There is a problem with sub-process"
    pass


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'] = ''
  try:
    if os.access(command, os.X_OK):
      print '\n', subprocess.check_output([command])
    elif os.access(command, os.R_OK):
      print open(command).read()
    else:
      raise OSError
  except (subprocess.CalledProcessError, OSError) as e:
    print "<p>Error :</p><pre>%s</pre>" % e


def make_menu():
  # Transform deep-2 tree in json
  folder_list = {}
  for folder in os.listdir(cgi_path):
    if os.path.isdir(os.path.join(cgi_path, folder)):
      folder_list[folder] = []
  for folder in folder_list:
    for file in os.listdir(os.path.join(cgi_path, folder)):
      if os.path.isfile(os.path.join(cgi_path, folder, file)):
        folder_list[folder].append(file)
  return folder_list


def get_cookie_password():
  cookie_string = os.environ.get('HTTP_COOKIE')
  if cookie_string:
    cookie.load(cookie_string)
    try:
      return cookie['password'].value
    except KeyError:
      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 is_password_set():
  return_document(monitor_password_script_path)
elif not check_password(password):
  print "<html><head>"
  print """
    <link rel="stylesheet" href="static/pure-min.css">
    <link rel="stylesheet" href="static/style.css">"""
  print "</head><body>"
  if password is None:
    print "<h1>This is the monitoring interface</h1>"
  else:
    print "<h1>Error</h1><p>Wrong password</p>"
  print """
  <p>Please enter the monitor_password in the next field to access the data</p>
  <form action="/index.cgi" method="post" class="pure-form-aligned">
    Password : <input type="password" name="password">
    <button type="submit" class="pure-button pure-button-primary">Access</button>
  </form>
  </body></html>"""
# redirection to the required script/page
else:
  print
  if "posting-script" in form:
    forward_form()
  elif "script" in form:
    return_document()
  else:
    html_base = jinja2.Template(open('{{ index_template }}').read())
    print
    print html_base.render(tree=make_menu(), default_page="{{ default_page }}", monitor_rewrite=monitor_rewrite)