Пример #1
0
class Spiff(object):
    def __init__(self, request):
        self.request        = request
        self.guard          = None
        self.current_user   = None
        self.requested_page = None
        self.bench          = Benchmarker()
        self.output         = ''
        #self.db.echo = 1


    def get_guard(self):
        if self.guard is None:
            db         = create_engine(config.cfg.get('database', 'dbn'))
            self.guard = SpiffGuard.DB(db)
        return self.guard


    def get_env(self, name): #FIXME: Probably shouldn't be here.
        return self.request.get_env(name)


    def set_requested_page(self, page):
        self.requested_page = page


    def get_requested_page(self):
        return self.requested_page


    def get_requested_uri(self, **kwargs):
        return self.request.get_current_url(**kwargs).get_string()


    def _get_permission_hash(self, user):
        """
        This function returns a string that identifies all permissions
        of the given user on the current group.
        FIXME: this sounds like a hack, and should not be here anyway.
        """
        page   = self.get_requested_page()
        string = page.get_attribute('private') and 'p' or 'np'
        if user is None:
            return string
        acls = self.guard.get_permission_list_with_inheritance(actor    = user,
                                                               resource = page)
        for acl in acls:
            string += str(acl)
        return sha.new(string).hexdigest()


    def login(self, username, password):
        """
        Attempts to login the user with the given name/password.
        """
        if username is None or password is None:
            return None
        user = self.guard.get_resource(handle = username, type = User)
        if user is None:
            return None
        if user.is_inactive():
            return None
        if not user.has_password(password):
            return None
        permission_key = self._get_permission_hash(user)
        self.request.get_session().data().set('user_id',        user.get_id())
        self.request.get_session().data().set('permission_key', permission_key)
        self.current_user = user
        return user


    def logout(self):
        self.request.get_session().destroy()
        self.current_user = None


    def get_current_user(self):
        if self.current_user is not None:
            return self.current_user
        session = self.request.get_session()
        if session is None:
            return None
        sid = session.get_id()
        assert sid is not None
        user_id = session.data().get('user_id')
        if user_id is None:
            return None
        user = self.guard.get_resource(id = user_id, type = User)
        if user is None:
            return None
        self.current_user = user
        return self.current_user


    def current_user_may(self, action_handle, page = None):
        if page is None:
            page = self.get_requested_page()

        # If the page is publicly available there's no need to ask the DB.
        private = page.get_attribute('private') or False
        if action_handle == 'view' and not private:
            return True

        # Get the currently logged in user.
        user = self.get_current_user()
        if user is None:
            return False

        # Ask the DB whether permission shall be granted.
        action = self.guard.get_action(type   = PageAction,
                                       handle = action_handle)
        if self.guard.has_permission(user, action, page):
            return True
        return False 


    def _render_text_template(self, filename, **kwargs):
        loader       = TemplateLoader(['web'])
        tmpl         = loader.load(filename, None, TextTemplate)
        self.output += tmpl.generate(web_dir      = '/web',
                                     current_user = self.get_current_user(),
                                     txt          = gettext,
                                     **kwargs).render('text')


    def _render_xhtml_template(self, filename, **kwargs):
        loader       = TemplateLoader(['web'])
        tmpl         = loader.load(filename, None, MarkupTemplate)
        self.output += tmpl.generate(web_dir      = '/web',
                                     uri          = self.get_requested_uri,
                                     request_uri  = self.get_requested_uri,
                                     current_user = self.get_current_user(),
                                     txt          = gettext,
                                     **kwargs).render('xhtml')


    def _render_head(self):
        self._render_text_template('header.tmpl', styles = [])
        if self.current_user_may('edit'):
            self._render_xhtml_template('admin_header.tmpl', may_edit_page = True)
        self._render_xhtml_template('header2.tmpl')


    def _render_footer(self):
        self._render_text_template('footer.tmpl',
                                   version = 'Spiff %s' % config.__version__,
                                   adapter = self.request.get_name())
        self.bench.snapshot('render_footer', 'Footer rendered in %ss.')
        self.bench.snapshot_total('total', 'Total rendering time is %ss.')
        self.request.write(self.output)
        self.request.write(self.bench.get_html())


    def _check_configured(self):
        if os.path.exists(config.cfg_file):
            return True
        self.request.write('Configuration file (%s) not found.' % config.cfg_file)
        self.request.write(' Please configure Spiff before accessing this')
        self.request.write(' site.<br/>')
        self.request.write('The INSTALL file shipped with the Spiff')
        self.request.write(' installation contains instructions on how this')
        self.request.write(' is done.')
        return False


    def _check_installer_deleted(self):
        if config.cfg.has_option('debug', 'ignore_installer_directory'):
            return True
        if not os.path.exists(config.installer_dir):
            return True
        self.request.write('Out of security reasons, please delete the')
        self.request.write(' installer directory (%s).' % config.installer_dir)
        return False


    def refer_to(self, url):
        self.request.set_status(301)
        self.request.add_header('Location', url.get_string())


    def run(self):
        if not self._check_configured():
            return
        if not self._check_installer_deleted():
            return

        # Set up the session and database adapters.
        self.request.start_session()
        page_db = PageDB(self.get_guard())
        user_db = UserDB(self.get_guard())
        cache   = CacheDB(self, self.get_guard())
        self.bench.snapshot('set_up', 'Set-up time is %ss.')

        # Can not open some pages by addressing them directly.
        page_handle = self.request.get_data().get_str('page')
        if page_db.is_system_page_handle(page_handle):
            self.request.set_status(403)
            self.request.write('%s is a system page.' % repr(page_handle))
            return

        # Find the current page using the given cgi variables.
        # If the specific site is not found, attempt to find a parent that
        # handles content recursively.
        page = page_db.get_responsible_page(page_handle)
        self.bench.snapshot('page_find', 'Looked up the page in %ss.')

        # If we still have no page, give 404.
        if page is None:
            self.request.set_status(404)
            self.request.write('Default page not found.')
            return
        self.set_requested_page(page)
        self.bench.snapshot('page_open', 'Opened the page in %ss.')

        # If the output of ALL extensions is cached (combined), there is no need
        # to redraw the page, including headers and footer.
        # Note that the cache only returns pages corresponding to the permissions
        # of the current user, so this is safe.
        if not self.request.has_post_data():
            output = cache.get_page()
            self.bench.snapshot('cache_check', 'Spent %ss checking the cache.')
            if output is not None:
                self.request.write(output)
                self._render_footer()
                return
        self.bench.snapshot('cache_check', 'Spent %ss checking the cache.')

        # Set up the plugin manager (Integrator). Note that the constructor
        # of PackageManager also associates the api with a reference to the
        # PackageManager instance.
        api = ExtensionApi(self,
                           guard   = self.get_guard(),
                           page_db = page_db,
                           cache   = cache,
                           request = self.request)
        pm = PackageManager(self.get_guard(), api, package = SpiffPackage)
        pm.set_package_dir(config.package_dir)
        self.bench.snapshot('package_load', 'Package manager loaded in %ss.')

        # Ending up here the entire page was not cached.
        # Make sure that the caller has permission to retrieve this page.
        if page.get_attribute('private') and not self.current_user_may('view'):
            url = self.request.get_url(page     = 'admin/login',
                                       refer_to = self.get_requested_uri())
            return self.refer_to(url)
        self.bench.snapshot('permission_check', 'Permission checked in %ss.')

        # Render the HTML headers.
        self._render_head()
        self.bench.snapshot('render_header', 'Headers rendered in %ss.')

        # Render the layout. This also invokes the plugins.
        self.output += page.get_output(api)
        self.bench.snapshot('render_plugins', 'Plugins rendered in %ss.')

        # Cache the page (if it is cacheable).
        if page.is_cacheable() and len(self.request.get_headers()) == 0:
            cache.add_page(self.output)
        self.bench.snapshot('cache_add', 'Added to cache in %ss.')

        # Yippie.
        self.request.write(self.output)
        self._render_footer()