Example #1
0
    def h_static(self, fname):
        """Serve static files"""
        form_config = self.scriptform.get_form_config()

        if not form_config.static_dir:
            raise HTTPError(501, "Static file serving not enabled")

        if '..' in fname:
            raise HTTPError(403, "Invalid file name")

        path = os.path.join(form_config.static_dir, fname)
        if not os.path.exists(path):
            raise HTTPError(404, "Not found")

        static_file = file(path, 'r')
        self.send_response(200)
        self.end_headers()
        self.wfile.write(static_file.read())
Example #2
0
    def auth(self):
        """
        Verify that the user is authenticated. This is required if the form
        definition contains a 'users' field (unless pre-auth from a front-end
        such as Apache is used). Returns the username if the user is validated
        or None if no validation is required. Otherwise, raises a 401 HTTP
        back to the client.
        """
        form_config = self.scriptform.get_form_config()
        username = None

        # Allow pre-auth from e.g. Apache htauth
        if 'REMOTE_USER' in self.headers:
            username = self.headers.get('REMOTE_USER')
            return self.headers.get('REMOTE_USER')

        # If a 'users' element was present in the form configuration file, the
        # user must be authenticated.
        if form_config.users:
            auth_header = self.headers.getheader("Authorization")
            if auth_header is not None:
                # Validate the username and password
                auth_unpw = auth_header.split(' ', 1)[1]
                username, password = base64.decodestring(auth_unpw).split(":",
                                                                          1)
                pw_hash = hashlib.sha256(password).hexdigest()

                if username in form_config.users and \
                   pw_hash == form_config.users[username]:
                    # Valid username and password. Return the username.
                    return username

            # Authentication needed, but not provided or wrong username/pw.
            headers = {"WWW-Authenticate": 'Basic realm="Private Area"'}
            raise HTTPError(401, 'Authenticate', headers)

        # No authentication required. Return None as the username.
        return None
Example #3
0
    def h_submit(self, form_values):
        """
        Handle the submitting of a form by validating the values and then doing
        a callback to a script. How the output is handled depends on settings
        in the form definition.
        """
        username = self.auth()

        form_config = self.scriptform.get_form_config()
        form_name = form_values.getfirst('form_name', None)
        form_def = form_config.get_form_def(form_name)
        if form_def.allowed_users is not None and \
           username not in form_def.allowed_users:
            raise HTTPError(403, "You're not authorized to view this form")

        # Convert FieldStorage to a simple dict, because we're not allowd to
        # add items to it. For normal fields, the form field name becomes the
        # key and the value becomes the field value. For file upload fields, we
        # stream the uploaded file to a temp file and then put the temp file in
        # the destination dict. We also add an extra field with the originally
        # uploaded file's name.
        values = {}
        tmp_files = []
        for field_name in form_values:
            field = form_values[field_name]
            if field.filename is not None:
                # Field is an uploaded file. Stream it to a temp file if
                # something was actually uploaded
                if field.filename == '':
                    continue
                tmp_fname = tempfile.mktemp(prefix="scriptform_")
                tmp_file = file(tmp_fname, 'w')
                while True:
                    buf = field.file.read(1024 * 16)
                    if not buf:
                        break
                    tmp_file.write(buf)
                tmp_file.close()
                field.file.close()

                tmp_files.append(tmp_fname)  # For later cleanup
                values[field_name] = tmp_fname
                values['{0}__name'.format(field_name)] = field.filename
            else:
                # Field is a normal form field. Store its value.
                values[field_name] = form_values.getfirst(field_name, None)

        form_errors, form_values = form_def.validate(values)

        if not form_errors:
            # Call script. If a result is returned, we wrap its output in some
            # nice HTML. If no result is returned, the output was raw and the
            # callback should have written its own response to the self.wfile
            # filehandle.

            # Log the callback and its parameters for auditing purposes.
            log = logging.getLogger('CALLBACK_AUDIT')
            cwd = os.path.realpath(os.curdir)
            log.info("Calling script: %s", form_def.script)
            log.info("Current working dir: %s", cwd)
            log.info("User: %s", username)
            log.info("Vars: %s", censor_form_values(form_def, form_values))

            form_def = form_config.get_form_def(form_name)

            # Construct base environment. The field values are added in
            # run_scripts.
            env = os.environ.copy()
            env["__SF__FORM"] = form_name
            if username is not None:
                env["__SF__USER"] = username

            result = runscript.run_script(form_def, form_values, env,
                                          self.wfile, self.wfile)
            if form_def.output != 'raw':
                # Ignore everything if we're doing raw output, since it's the
                # scripts responsibility.
                if result['exitcode'] != 0:
                    stderr = cgi.escape(result['stderr'].decode('utf8'))
                    msg = u'<span class="error">{0}</span>'.format(stderr)
                else:
                    if form_def.output == 'escaped':
                        stdout = cgi.escape(result['stdout'].decode('utf8'))
                        msg = u'<pre>{0}</pre>'.format(stdout)
                    else:
                        # Non-escaped output (html, usually)
                        msg = result['stdout'].decode('utf8')

                output = HTML_SUBMIT_RESPONSE.format(
                    header=HTML_HEADER.format(
                        title=form_config.title,
                        custom_css=form_config.custom_css
                    ),
                    footer=HTML_FOOTER,
                    title=form_def.title,
                    form_name=form_def.name,
                    msg=msg,
                )
                self.send_response(200)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                self.wfile.write(output.encode('utf8'))
        else:
            # Form had errors
            form_values.pop('form_name')
            self.h_form(form_name, form_errors, **form_values)

        # Clean up uploaded files
        for file_name in tmp_files:
            if os.path.exists(file_name):
                os.unlink(file_name)
Example #4
0
    def h_form(self, form_name, errors=None, **form_values):
        """
        Render a form.
        """
        def render_field(field, errors):
            """
            Render a HTML field.
            """
            params = {
                'name': field['name'],
                'classes': [],
            }

            if field.get('hidden', None):
                params['classes'].append('hidden')

            if field.get('required', None):
                params['classes'].append('required')

            params['classes'].extend(field.get('classes', '').split())

            params["style"] = field.get("style", "")

            # Get field-specific parameters
            if field['type'] not in ('file', 'checkbox'):
                default_value = field.get('default_value', '')
                params['value'] = form_values.get(field['name'], default_value)

            if field['type'] not in ('radio', 'checkbox', 'select'):
                params['required'] = field.get('required', False)

            if field['type'] == 'string':
                params['size'] = field.get('size', '')

            if field['type'] in ('string', 'password', 'text'):
                params['minlen'] = field.get('minlen', '')

            if field['type'] in ('string', 'text'):
                params['maxlen'] = field.get('maxlen', '')

            if field['type'] in ('integer', 'float'):
                params['minval'] = field.get('min', '')
                params['maxval'] = field.get('max', '')

            if field['type'] == 'date':
                params['minval'] = field.get('min', '')
                params['maxval'] = field.get('max', '')

            if field['type'] == 'text':
                params['rows'] = field.get('rows', '')
                params['cols'] = field.get('cols', '')

            if field['type'] == 'radio':
                if 'options_from' in field:
                    fname = field['options_from']
                    options = runscript.from_file(fname)
                else:
                    options = field['options']

                if not form_values.get(field['name'], None):
                    # Set default value
                    params['value'] = options[0][0]

            if field['type'] in ('radio', 'select'):
                if 'options_from' in field:
                    fname = field['options_from']
                    params['options'] = runscript.from_file(fname)
                else:
                    params['options'] = field['options']

            if field['type'] == 'checkbox':
                # Set default value from field definition
                params['checked'] = False
                if 'checked' in field and field['checked']:
                    params['checked'] = True

                # Set value from submitted form if applicable
                if field['name'] in form_values:
                    if form_values[field['name']] == 'on':
                        params['checked'] = True
                    else:
                        params['checked'] = False

            h_input = fr_inst.r_field(field['type'], **params)

            return fr_inst.r_form_line(field['type'], field['title'],
                                       h_input, params['classes'], errors)

        if errors is None:
            errors = {}

        username = self.auth()
        form_config = self.scriptform.get_form_config()
        fr_inst = FormRender(None)

        # Make sure the user is allowed to access this form.
        form_def = form_config.get_form_def(form_name)
        if form_def.allowed_users is not None and \
           username not in form_def.allowed_users:
            raise HTTPError(403, "You're not authorized to view this form")

        html_errors = u''
        if errors:
            html_errors = u'<ul>'
            for error in errors:
                html_errors += u'<li class="error">{0}</li>'.format(error)
            html_errors += u'</ul>'

        output = HTML_FORM.format(
            header=HTML_HEADER.format(title=form_config.title,
                                      custom_css=form_config.custom_css),
            footer=HTML_FOOTER,
            title=form_def.title,
            description=form_def.description,
            errors=html_errors,
            name=form_def.name,
            fields=u''.join(
                [render_field(f, errors.get(f['name'], []))
                 for f in form_def.get_fields()]
            ),
            submit_title=form_def.submit_title
        )
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(output.encode('utf8'))