Esempio n. 1
0
    def __get_desc(self, cs):
        desc_msg = [(_('%s committed on %s') %
                     (h.person(cs.author), h.fmt_date(cs.date))) + '<br/>']
        #branches, tags, bookmarks
        if cs.branch:
            desc_msg.append('branch: %s<br/>' % cs.branch)
        if h.is_hg(c.db_repo_scm_instance):
            for book in cs.bookmarks:
                desc_msg.append('bookmark: %s<br/>' % book)
        for tag in cs.tags:
            desc_msg.append('tag: %s<br/>' % tag)
        diff_processor, changes = self.__changes(cs)
        # rev link
        _url = h.canonical_url('changeset_home',
                               repo_name=c.db_repo.repo_name,
                               revision=cs.raw_id)
        desc_msg.append('changeset: <a href="%s">%s</a>' %
                        (_url, cs.raw_id[:8]))

        desc_msg.append('<pre>')
        desc_msg.append(h.urlify_text(cs.message))
        desc_msg.append('\n')
        desc_msg.extend(changes)
        if str2bool(CONFIG.get('rss_include_diff', False)):
            desc_msg.append('\n\n')
            desc_msg.append(diff_processor.as_raw())
        desc_msg.append('</pre>')
        return map(safe_unicode, desc_msg)
Esempio n. 2
0
def fixups(models, _SESSION):
    from pylons import config

    notify('migrating options from .ini file')
    use_gravatar = str2bool(config.get('use_gravatar'))
    print('Setting gravatar use to: %s' % use_gravatar)
    sett = models.Setting.create_or_update('use_gravatar',
                                                    use_gravatar, 'bool')
    _SESSION().add(sett)
    _SESSION.commit()
    #set the new format of gravatar URL
    gravatar_url = models.User.DEFAULT_GRAVATAR_URL
    if config.get('alternative_gravatar_url'):
        gravatar_url = config.get('alternative_gravatar_url')

    print('Setting gravatar url to:%s' % gravatar_url)
    sett = models.Setting.create_or_update('gravatar_url',
                                                    gravatar_url, 'unicode')
    _SESSION().add(sett)
    _SESSION.commit()

    #now create new changed value of clone_url
    clone_uri_tmpl = models.Repository.DEFAULT_CLONE_URI
    print('settings new clone url template to %s' % clone_uri_tmpl)

    sett = models.Setting.create_or_update('clone_uri_tmpl',
                                                    clone_uri_tmpl, 'unicode')
    _SESSION().add(sett)
    _SESSION.commit()
Esempio n. 3
0
    def __get_desc(self, cs):
        desc_msg = [(_('%s committed on %s')
                     % (h.person(cs.author), h.fmt_date(cs.date))) + '<br/>']
        #branches, tags, bookmarks
        if cs.branch:
            desc_msg.append('branch: %s<br/>' % cs.branch)
        if h.is_hg(c.db_repo_scm_instance):
            for book in cs.bookmarks:
                desc_msg.append('bookmark: %s<br/>' % book)
        for tag in cs.tags:
            desc_msg.append('tag: %s<br/>' % tag)
        diff_processor, changes = self.__changes(cs)
        # rev link
        _url = h.canonical_url('changeset_home', repo_name=c.db_repo.repo_name,
                   revision=cs.raw_id)
        desc_msg.append('changeset: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))

        desc_msg.append('<pre>')
        desc_msg.append(h.urlify_text(cs.message))
        desc_msg.append('\n')
        desc_msg.extend(changes)
        if str2bool(CONFIG.get('rss_include_diff', False)):
            desc_msg.append('\n\n')
            desc_msg.append(diff_processor.as_raw())
        desc_msg.append('</pre>')
        return map(safe_unicode, desc_msg)
Esempio n. 4
0
    def __before__(self):
        """
        __before__ is called before controller methods and after __call__
        """
        c.kallithea_version = __version__
        rc_config = Setting.get_app_settings()

        # Visual options
        c.visual = AttributeDict({})

        ## DB stored
        c.visual.show_public_icon = str2bool(rc_config.get('show_public_icon'))
        c.visual.show_private_icon = str2bool(rc_config.get('show_private_icon'))
        c.visual.stylify_metatags = str2bool(rc_config.get('stylify_metatags'))
        c.visual.dashboard_items = safe_int(rc_config.get('dashboard_items', 100))
        c.visual.admin_grid_items = safe_int(rc_config.get('admin_grid_items', 100))
        c.visual.repository_fields = str2bool(rc_config.get('repository_fields'))
        c.visual.show_version = str2bool(rc_config.get('show_version'))
        c.visual.use_gravatar = str2bool(rc_config.get('use_gravatar'))
        c.visual.gravatar_url = rc_config.get('gravatar_url')

        c.ga_code = rc_config.get('ga_code')
        # TODO: replace undocumented backwards compatibility hack with db upgrade and rename ga_code
        if c.ga_code and '<' not in c.ga_code:
            c.ga_code = '''<script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', '%s']);
                _gaq.push(['_trackPageview']);

                (function() {
                    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                    })();
            </script>''' % c.ga_code
        c.site_name = rc_config.get('title')
        c.clone_uri_tmpl = rc_config.get('clone_uri_tmpl')

        ## INI stored
        c.visual.allow_repo_location_change = str2bool(config.get('allow_repo_location_change', True))
        c.visual.allow_custom_hooks_settings = str2bool(config.get('allow_custom_hooks_settings', True))

        c.instance_id = config.get('instance_id')
        c.issues_url = config.get('bugtracker', url('issues_url'))
        # END CONFIG VARS

        c.repo_name = get_repo_slug(request)  # can be empty
        c.backends = BACKENDS.keys()
        c.unread_notifications = NotificationModel() \
                        .get_unread_cnt_for_user(c.authuser.user_id)

        self.cut_off_limit = safe_int(config.get('cut_off_limit'))

        c.my_pr_count = PullRequestModel().get_pullrequest_cnt_for_user(c.authuser.user_id)

        self.sa = meta.Session
        self.scm_model = ScmModel(self.sa)
Esempio n. 5
0
    def __call__(self, environ, start_response):
        self.__fixup(environ)
        debug = str2bool(self.config.get('debug'))
        is_ssl = environ['wsgi.url_scheme'] == 'https'

        def custom_start_response(status, headers, exc_info=None):
            if is_ssl and str2bool(self.config.get('use_htsts')) and not debug:
                headers.append(('Strict-Transport-Security',
                                'max-age=8640000; includeSubDomains'))
            return start_response(status, headers, exc_info)

        return self.application(environ, custom_start_response)
Esempio n. 6
0
def ssh_serve(user_id, key_id):
    """Serve SSH repository protocol access.

    The trusted command that is invoked from .ssh/authorized_keys to serve SSH
    protocol access. The access will be granted as the specified user ID, and
    logged as using the specified key ID.
    """
    ssh_enabled = kallithea.CONFIG.get('ssh_enabled', False)
    if not str2bool(ssh_enabled):
        sys.stderr.write("SSH access is disabled.\n")
        return sys.exit(1)

    ssh_locale = kallithea.CONFIG.get('ssh_locale')
    if ssh_locale:
        os.environ[
            'LC_ALL'] = ssh_locale  # trumps everything, including LANG, except LANGUAGE
        os.environ[
            'LANGUAGE'] = ssh_locale  # trumps LC_ALL for GNU gettext message handling

    ssh_original_command = os.environ.get('SSH_ORIGINAL_COMMAND', '')
    client_ip = os.environ.get('SSH_CONNECTION', '').split(' ',
                                                           1)[0] or '0.0.0.0'
    log.debug('ssh-serve was invoked for SSH command %r from %s',
              ssh_original_command, client_ip)

    if not ssh_original_command:
        if os.environ.get('SSH_CONNECTION'):
            sys.stderr.write(
                "'kallithea-cli ssh-serve' can only provide protocol access over SSH. Interactive SSH login for this user is disabled.\n"
            )
        else:
            sys.stderr.write(
                "'kallithea-cli ssh-serve' cannot be called directly. It must be specified as command in an SSH authorized_keys file.\n"
            )
        return sys.exit(1)

    try:
        ssh_command_parts = shlex.split(ssh_original_command)
    except ValueError as e:
        sys.stderr.write('Error parsing SSH command %r: %s\n' %
                         (ssh_original_command, e))
        sys.exit(1)
    for VcsHandler in [MercurialSshHandler, GitSshHandler]:
        vcs_handler = VcsHandler.make(ssh_command_parts)
        if vcs_handler is not None:
            vcs_handler.serve(user_id, key_id, client_ip)
            assert False  # serve is written so it never will terminate

    sys.stderr.write(
        "This account can only be used for repository access. SSH command %r is not supported.\n"
        % ssh_original_command)
    sys.exit(1)
Esempio n. 7
0
 def _check_ssl(self, environ):
     """
     Checks the SSL check flag and returns False if SSL is not present
     and required True otherwise
     """
     #check if we have SSL required  ! if not it's a bad request !
     if str2bool(Ui.get_by_key('web', 'push_ssl').ui_value):
         org_proto = environ.get('wsgi._org_proto', environ['wsgi.url_scheme'])
         if org_proto != 'https':
             log.debug('proto is %s and SSL is required BAD REQUEST !',
                       org_proto)
             return False
     return True
Esempio n. 8
0
 def __before__(self):
     super(FeedController, self).__before__()
     #common values for feeds
     self.description = _('Changes on %s repository')
     self.title = self.title = _('%s %s feed') % (c.site_name, '%s')
     self.language = 'en-us'
     self.ttl = "5"
     import kallithea
     CONF = kallithea.CONFIG
     self.include_diff = str2bool(CONF.get('rss_include_diff', False))
     self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
     # we need to protect from parsing huge diffs here other way
     # we can kill the server
     self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit', 32 * 1024))
Esempio n. 9
0
    def command(self):
        from pylons import config
        try:
            CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
        except KeyError:
            CELERY_ON = False

        if not CELERY_ON:
            raise Exception('Please set use_celery = true in .ini config '
                            'file before running celeryd')
        kallithea.CELERY_ON = CELERY_ON
        load_rcextensions(config['here'])
        cmd = self.celery_command(app_or_default())
        return cmd.run(**vars(self.options))
Esempio n. 10
0
 def _check_ssl(self, environ):
     """
     Checks the SSL check flag and returns False if SSL is not present
     and required True otherwise
     """
     #check if we have SSL required  ! if not it's a bad request !
     if str2bool(Ui.get_by_key('push_ssl').ui_value):
         org_proto = environ.get('wsgi._org_proto',
                                 environ['wsgi.url_scheme'])
         if org_proto != 'https':
             log.debug('proto is %s and SSL is required BAD REQUEST !' %
                       org_proto)
             return False
     return True
Esempio n. 11
0
    def command(self):
        from pylons import config
        try:
            CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
        except KeyError:
            CELERY_ON = False

        if not CELERY_ON:
            raise Exception('Please set use_celery = true in .ini config '
                            'file before running celeryd')
        kallithea.CELERY_ON = CELERY_ON
        load_rcextensions(config['here'])
        cmd = self.celery_command(app_or_default())
        return cmd.run(**vars(self.options))
Esempio n. 12
0
 def __before__(self):
     super(FeedController, self).__before__()
     #common values for feeds
     self.description = _('Changes on %s repository')
     self.title = self.title = _('%s %s feed') % (c.site_name, '%s')
     self.language = 'en-us'
     self.ttl = "5"
     import kallithea
     CONF = kallithea.CONFIG
     self.include_diff = str2bool(CONF.get('rss_include_diff', False))
     self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
     # we need to protect from parsing huge diffs here other way
     # we can kill the server
     self.feed_diff_limit = safe_int(
         CONF.get('rss_cut_off_limit', 32 * 1024))
Esempio n. 13
0
def show_id(cs):
    """
    Configurable function that shows ID
    by default it's r123:fffeeefffeee

    :param cs: changeset instance
    """
    from kallithea import CONFIG
    def_len = safe_int(CONFIG.get('show_sha_length', 12))
    show_rev = str2bool(CONFIG.get('show_revision_number', False))

    raw_id = cs.raw_id[:def_len]
    if show_rev:
        return 'r%s:%s' % (cs.revision, raw_id)
    else:
        return raw_id
Esempio n. 14
0
def fixups(models, _SESSION):
    notify('Fixing default auth modules')
    plugins = 'kallithea.lib.auth_modules.auth_internal'
    opts = []
    ldap_enabled = str2bool(
        getattr(models.Setting.get_by_name('ldap_active'),
                'app_settings_value', False))
    if ldap_enabled:
        plugins += ',kallithea.lib.auth_modules.auth_ldap'
        opts.append(('auth_ldap_enabled', 'True', 'bool'))

    opts.append(('auth_plugins', plugins, 'list'), )
    opts.append(('auth_internal_enabled', 'True', 'bool'))

    for name, default, type_ in opts:
        setting = models.Setting.get_by_name(name)
        if not setting:
            # if we don't have this option create it
            setting = models.Setting(name, default, type_)

        _SESSION().add(setting)
        _SESSION().commit()

    #copy over the LDAP settings
    old_ldap = [
        ('ldap_active', 'false', 'bool'), ('ldap_host', '', 'unicode'),
        ('ldap_port', '389', 'int'), ('ldap_tls_kind', 'PLAIN', 'unicode'),
        ('ldap_tls_reqcert', '', 'unicode'), ('ldap_dn_user', '', 'unicode'),
        ('ldap_dn_pass', '', 'unicode'), ('ldap_base_dn', '', 'unicode'),
        ('ldap_filter', '', 'unicode'), ('ldap_search_scope', '', 'unicode'),
        ('ldap_attr_login', '', 'unicode'),
        ('ldap_attr_firstname', '', 'unicode'),
        ('ldap_attr_lastname', '', 'unicode'),
        ('ldap_attr_email', '', 'unicode')
    ]
    for k, v, t in old_ldap:
        old_setting = models.Setting.get_by_name(k)
        name = 'auth_%s' % k
        setting = models.Setting.get_by_name(name)
        if not setting:
            # if we don't have this option create it
            setting = models.Setting(name, old_setting.app_settings_value, t)

        _SESSION().add(setting)
        _SESSION().commit()
Esempio n. 15
0
def setup_application(app):
    config = app.config

    # we want our low level middleware to get to the request ASAP. We don't
    # need any stack middleware in them - especially no StatusCodeRedirect buffering
    app = SimpleHg(app, config)
    app = SimpleGit(app, config)

    # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
    if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
        app = HttpsFixup(app, config)

    app = PermanentRepoUrl(app, config)

    # Optional and undocumented wrapper - gives more verbose request/response logging, but has a slight overhead
    if str2bool(config.get('use_wsgi_wrapper')):
        app = RequestWrapper(app, config)

    return app
Esempio n. 16
0
    def write_authorized_keys(self):
        if not str2bool(config.get('ssh_enabled', False)):
            log.error("Will not write SSH authorized_keys file - ssh_enabled is not configured")
            return
        authorized_keys = config.get('ssh_authorized_keys')
        kallithea_cli_path = config.get('kallithea_cli_path', 'kallithea-cli')
        if not authorized_keys:
            log.error('Cannot write SSH authorized_keys file - ssh_authorized_keys is not configured')
            return
        log.info('Writing %s', authorized_keys)

        authorized_keys_dir = os.path.dirname(authorized_keys)
        try:
            os.makedirs(authorized_keys_dir)
            os.chmod(authorized_keys_dir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) # ~/.ssh/ must be 0700
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise
        # Now, test that the directory is or was created in a readable way by previous.
        if not (os.path.isdir(authorized_keys_dir) and
                os.access(authorized_keys_dir, os.W_OK)):
            raise SshKeyModelException("Directory of authorized_keys cannot be written to so authorized_keys file %s cannot be written" % (authorized_keys))

        # Make sure we don't overwrite a key file with important content
        if os.path.exists(authorized_keys):
            with open(authorized_keys) as f:
                for l in f:
                    if not l.strip() or l.startswith('#'):
                        pass # accept empty lines and comments
                    elif ssh.SSH_OPTIONS in l and ' ssh-serve ' in l:
                        pass # Kallithea entries are ok to overwrite
                    else:
                        raise SshKeyModelException("Safety check failed, found %r line in %s - please remove it if Kallithea should manage the file" % (l.strip(), authorized_keys))

        fh, tmp_authorized_keys = tempfile.mkstemp('.authorized_keys', dir=os.path.dirname(authorized_keys))
        with os.fdopen(fh, 'w') as f:
            f.write("# WARNING: This .ssh/authorized_keys file is managed by Kallithea. Manual editing or adding new entries will make Kallithea back off.\n")
            for key in UserSshKeys.query().join(UserSshKeys.user).filter(User.active == True):
                f.write(ssh.authorized_keys_line(kallithea_cli_path, config['__file__'], key))
        os.chmod(tmp_authorized_keys, stat.S_IRUSR | stat.S_IWUSR)
        # Note: simple overwrite / rename isn't enough to replace the file on Windows
        os.replace(tmp_authorized_keys, authorized_keys)
Esempio n. 17
0
    def _get_hg_ui_settings(self):
        ret = Ui.query().all()

        settings = {}
        for each in ret:
            k = each.ui_section + '_' + each.ui_key
            v = each.ui_value
            if k == 'paths_/':
                k = 'paths_root_path'

            if k == 'web_push_ssl':
                v = str2bool(v)

            k = k.replace('.', '_')

            if each.ui_section in ['hooks', 'extensions']:
                v = each.ui_active

            settings[k] = v
        return settings
Esempio n. 18
0
def fixups(models, _SESSION):
    notify('Fixing default auth modules')
    plugins = 'kallithea.lib.auth_modules.auth_internal'
    opts = []
    ldap_enabled = str2bool(getattr(
        models.Setting.get_by_name('ldap_active'),
        'app_settings_value', False))
    if ldap_enabled:
        plugins += ',kallithea.lib.auth_modules.auth_ldap'
        opts.append(('auth_ldap_enabled', 'True', 'bool'))

    opts.append(('auth_plugins', plugins, 'list'),)
    opts.append(('auth_internal_enabled', 'True', 'bool'))

    for name, default, type_ in opts:
        setting = models.Setting.get_by_name(name)
        if not setting:
            # if we don't have this option create it
            setting = models.Setting(name, default, type_)

        _SESSION().add(setting)
        _SESSION().commit()

    #copy over the LDAP settings
    old_ldap = [('ldap_active', 'false', 'bool'), ('ldap_host', '', 'unicode'),
                ('ldap_port', '389', 'int'), ('ldap_tls_kind', 'PLAIN', 'unicode'),
                ('ldap_tls_reqcert', '', 'unicode'), ('ldap_dn_user', '', 'unicode'),
                ('ldap_dn_pass', '', 'unicode'), ('ldap_base_dn', '', 'unicode'),
                ('ldap_filter', '', 'unicode'), ('ldap_search_scope', '', 'unicode'),
                ('ldap_attr_login', '', 'unicode'), ('ldap_attr_firstname', '', 'unicode'),
                ('ldap_attr_lastname', '', 'unicode'), ('ldap_attr_email', '', 'unicode')]
    for k, v, t in old_ldap:
        old_setting = models.Setting.get_by_name(k)
        name = 'auth_%s' % k
        setting = models.Setting.get_by_name(name)
        if not setting:
            # if we don't have this option create it
            setting = models.Setting(name, old_setting.app_settings_value, t)

        _SESSION().add(setting)
        _SESSION().commit()
Esempio n. 19
0
    def take_action(self, args):
        from kallithea.lib import celerypylons
        from tg import config
        try:
            CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
        except KeyError:
            CELERY_ON = False

        if not CELERY_ON:
            raise Exception('Please set use_celery = true in .ini config '
                            'file before running celeryd')
        kallithea.CELERY_ON = CELERY_ON

        load_rcextensions(config['here'])
        cmd = celerypylons.worker.worker(celerypylons.app.app_or_default())

        celery_args = args.celery_args
        if '--' in celery_args:
            celery_args.remove('--')

        return cmd.run_from_argv('kallithea celery worker', celery_args)
Esempio n. 20
0
    def take_action(self, args):
        from kallithea.lib import celerypylons
        from tg import config
        try:
            CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
        except KeyError:
            CELERY_ON = False

        if not CELERY_ON:
            raise Exception('Please set use_celery = true in .ini config '
                            'file before running celeryd')
        kallithea.CELERY_ON = CELERY_ON

        load_rcextensions(config['here'])
        cmd = celerypylons.worker.worker(celerypylons.app.app_or_default())

        celery_args = args.celery_args
        if '--' in celery_args:
            celery_args.remove('--')

        return cmd.run_from_argv('kallithea celery worker', celery_args)
Esempio n. 21
0
    def _get_hg_ui_settings(self):
        ret = Ui.query().all()

        if not ret:
            raise Exception('Could not get application ui settings !')
        settings = {}
        for each in ret:
            k = each.ui_key
            v = each.ui_value
            if k == '/':
                k = 'root_path'

            if k == 'push_ssl':
                v = str2bool(v)

            if k.find('.') != -1:
                k = k.replace('.', '_')

            if each.ui_section in ['hooks', 'extensions']:
                v = each.ui_active

            settings[each.ui_section + '_' + k] = v
        return settings
Esempio n. 22
0
    def __fixup(self, environ):
        """
        Function to fixup the environ as needed. In order to use this
        middleware you should set this header inside your
        proxy ie. nginx, apache etc.
        """
        # DETECT PROTOCOL !
        if 'HTTP_X_URL_SCHEME' in environ:
            proto = environ.get('HTTP_X_URL_SCHEME')
        elif 'HTTP_X_FORWARDED_SCHEME' in environ:
            proto = environ.get('HTTP_X_FORWARDED_SCHEME')
        elif 'HTTP_X_FORWARDED_PROTO' in environ:
            proto = environ.get('HTTP_X_FORWARDED_PROTO')
        else:
            proto = 'http'
        org_proto = proto

        # if we have force, just override
        if str2bool(self.config.get('force_https')):
            proto = 'https'

        environ['wsgi.url_scheme'] = proto
        environ['wsgi._org_proto'] = org_proto
Esempio n. 23
0
    def _get_username(self, environ, settings):
        username = None
        environ = environ or {}
        if not environ:
            log.debug('got empty environ: %s' % environ)

        settings = settings or {}
        if settings.get('header'):
            header = settings.get('header')
            username = environ.get(header)
            log.debug('extracted %s:%s' % (header, username))

        # fallback mode
        if not username and settings.get('fallback_header'):
            header = settings.get('fallback_header')
            username = environ.get(header)
            log.debug('extracted %s:%s' % (header, username))

        if username and str2bool(settings.get('clean_username')):
            log.debug('Received username %s from container' % username)
            username = self._clean_username(username)
            log.debug('New cleanup user is: %s' % username)
        return username
Esempio n. 24
0
def authenticate(username, password, environ=None):
    """
    Authentication function used for access control,
    It tries to authenticate based on enabled authentication modules.

    :param username: username can be empty for container auth
    :param password: password can be empty for container auth
    :param environ: environ headers passed for container auth
    :returns: None if auth failed, user_data dict if auth is correct
    """

    auth_plugins = get_auth_plugins()
    for plugin in auth_plugins:
        module = plugin.__class__.__module__
        log.debug('Trying authentication using %s', module)
        # load plugin settings from Kallithea database
        plugin_name = plugin.name
        plugin_settings = {}
        for v in plugin.plugin_settings():
            conf_key = "auth_%s_%s" % (plugin_name, v["name"])
            setting = Setting.get_by_name(conf_key)
            plugin_settings[
                v["name"]] = setting.app_settings_value if setting else None
        log.debug('Settings for auth plugin %s:\n%s', plugin_name,
                  formatted_json(plugin_settings))

        if not str2bool(plugin_settings["enabled"]):
            log.info("Authentication plugin %s is disabled, skipping for %s",
                     module, username)
            continue

        # use plugin's method of user extraction.
        user = plugin.get_user(username,
                               environ=environ,
                               settings=plugin_settings)
        log.debug('Plugin %s extracted user `%s`', module, user)
        if not plugin.accepts(user):
            log.debug('Plugin %s does not accept user `%s` for authentication',
                      module, user)
            continue
        else:
            log.debug('Plugin %s accepted user `%s` for authentication',
                      module, user)
            # The user might have tried to authenticate using their email address,
            # then the username variable wouldn't contain a valid username.
            # But as the plugin has accepted the user, .username field should
            # have a valid username, so use it for authentication purposes.
            if user is not None:
                username = user.username

        log.info('Authenticating user using %s plugin', module)

        # _authenticate is a wrapper for .auth() method of plugin.
        # it checks if .auth() sends proper data. For KallitheaExternalAuthPlugin
        # it also maps users to Database and maps the attributes returned
        # from .auth() to Kallithea database. If this function returns data
        # then auth is correct.
        user_data = plugin._authenticate(user,
                                         username,
                                         password,
                                         plugin_settings,
                                         environ=environ or {})
        log.debug('Plugin user data: %s', user_data)

        if user_data is not None:
            log.debug('Plugin returned proper authentication data')
            return user_data

        # we failed to Auth because .auth() method didn't return the user
        if username:
            log.warning("User `%s` failed to authenticate against %s",
                        username, module)
    return None
Esempio n. 25
0
 def app_settings_value(self):
     v = self._app_settings_value
     if self.app_settings_name == 'ldap_active':
         v = str2bool(v)
     return v
Esempio n. 26
0
def load_environment(global_conf, app_conf, initial=False,
                     test_env=None, test_index=None):
    """
    Configure the Pylons environment via the ``pylons.config``
    object
    """
    config = pylons.configuration.PylonsConfig()

    # Pylons paths
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    paths = dict(
        root=root,
        controllers=os.path.join(root, 'controllers'),
        static_files=os.path.join(root, 'public'),
        templates=[os.path.join(root, 'templates')]
    )

    # Initialize config with the basic options
    config.init_app(global_conf, app_conf, package='kallithea', paths=paths)

    # store some globals into kallithea
    kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
    kallithea.CELERY_EAGER = str2bool(config['app_conf'].get('celery.always.eager'))

    config['routes.map'] = make_map(config)
    config['pylons.app_globals'] = app_globals.Globals(config)
    config['pylons.h'] = helpers
    kallithea.CONFIG = config

    load_rcextensions(root_path=config['here'])

    # Setup cache object as early as possible
    pylons.cache._push_object(config['pylons.app_globals'].cache)

    # Create the Mako TemplateLookup, with the default auto-escaping
    config['pylons.app_globals'].mako_lookup = mako.lookup.TemplateLookup(
        directories=paths['templates'],
        error_handler=pylons.error.handle_mako_error,
        module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
        input_encoding='utf-8', default_filters=['escape'],
        imports=['from webhelpers.html import escape'])

    # sets the c attribute access when don't existing attribute are accessed
    config['pylons.strict_tmpl_context'] = True
    test = os.path.split(config['__file__'])[-1] == 'test.ini'
    if test:
        if test_env is None:
            test_env = not int(os.environ.get('KALLITHEA_NO_TMP_PATH', 0))
        if test_index is None:
            test_index = not int(os.environ.get('KALLITHEA_WHOOSH_TEST_DISABLE', 0))
        if os.environ.get('TEST_DB'):
            # swap config if we pass enviroment variable
            config['sqlalchemy.db1.url'] = os.environ.get('TEST_DB')

        from kallithea.lib.utils import create_test_env, create_test_index
        from kallithea.tests import TESTS_TMP_PATH
        #set KALLITHEA_NO_TMP_PATH=1 to disable re-creating the database and
        #test repos
        if test_env:
            create_test_env(TESTS_TMP_PATH, config)
        #set KALLITHEA_WHOOSH_TEST_DISABLE=1 to disable whoosh index during tests
        if test_index:
            create_test_index(TESTS_TMP_PATH, config, True)

    DbManage.check_waitress()
    # MULTIPLE DB configs
    # Setup the SQLAlchemy database engine
    sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
    init_model(sa_engine_db1)

    set_available_permissions(config)
    repos_path = make_ui('db').configitems('paths')[0][1]
    config['base_path'] = repos_path
    set_app_settings(config)

    instance_id = kallithea.CONFIG.get('instance_id', '*')
    if instance_id == '*':
        instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
        kallithea.CONFIG['instance_id'] = instance_id

    # CONFIGURATION OPTIONS HERE (note: all config options will override
    # any Pylons config options)

    # store config reference into our module to skip import magic of
    # pylons
    kallithea.CONFIG.update(config)
    set_vcs_config(kallithea.CONFIG)
    set_indexer_config(kallithea.CONFIG)

    #check git version
    check_git_version()

    if str2bool(config.get('initial_repo_scan', True)):
        repo2db_mapper(ScmModel().repo_scan(repos_path),
                       remove_obsolete=False, install_git_hooks=False)
    formencode.api.set_stdtranslation(languages=[config.get('lang')])
    return config
Esempio n. 27
0
 def test_str2bool(self, str_bool, expected):
     from kallithea.lib.utils2 import str2bool
     assert str2bool(str_bool) == expected
Esempio n. 28
0
    def update(self, form_result):
        perm_user = User.get_by_username(username=form_result['perm_user_name'])

        try:
            # stage 1 set anonymous access
            if perm_user.username == User.DEFAULT_USER:
                perm_user.active = str2bool(form_result['anonymous'])
                self.sa.add(perm_user)

            # stage 2 reset defaults and set them from form data
            def _make_new(usr, perm_name):
                log.debug('Creating new permission:%s' % (perm_name))
                new = UserToPerm()
                new.user = usr
                new.permission = Permission.get_by_key(perm_name)
                return new
            # clear current entries, to make this function idempotent
            # it will fix even if we define more permissions or permissions
            # are somehow missing
            u2p = self.sa.query(UserToPerm)\
                .filter(UserToPerm.user == perm_user)\
                .all()
            for p in u2p:
                self.sa.delete(p)
            #create fresh set of permissions
            for def_perm_key in ['default_repo_perm',
                                 'default_group_perm',
                                 'default_user_group_perm',
                                 'default_repo_create',
                                 'create_on_write', # special case for create repos on write access to group
                                 #'default_repo_group_create', #not implemented yet
                                 'default_user_group_create',
                                 'default_fork',
                                 'default_register',
                                 'default_extern_activate']:
                p = _make_new(perm_user, form_result[def_perm_key])
                self.sa.add(p)

            #stage 3 update all default permissions for repos if checked
            if form_result['overwrite_default_repo']:
                _def_name = form_result['default_repo_perm'].split('repository.')[-1]
                _def = Permission.get_by_key('repository.' + _def_name)
                # repos
                for r2p in self.sa.query(UserRepoToPerm)\
                               .filter(UserRepoToPerm.user == perm_user)\
                               .all():

                    #don't reset PRIVATE repositories
                    if not r2p.repository.private:
                        r2p.permission = _def
                        self.sa.add(r2p)

            if form_result['overwrite_default_group']:
                _def_name = form_result['default_group_perm'].split('group.')[-1]
                # groups
                _def = Permission.get_by_key('group.' + _def_name)
                for g2p in self.sa.query(UserRepoGroupToPerm)\
                               .filter(UserRepoGroupToPerm.user == perm_user)\
                               .all():
                    g2p.permission = _def
                    self.sa.add(g2p)

            if form_result['overwrite_default_user_group']:
                _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1]
                # groups
                _def = Permission.get_by_key('usergroup.' + _def_name)
                for g2p in self.sa.query(UserUserGroupToPerm)\
                               .filter(UserUserGroupToPerm.user == perm_user)\
                               .all():
                    g2p.permission = _def
                    self.sa.add(g2p)

            self.sa.commit()
        except (DatabaseError,):
            log.error(traceback.format_exc())
            self.sa.rollback()
            raise
Esempio n. 29
0
    def _before(self, *args, **kwargs):
        """
        _before is called before controller methods and after __call__
        """
        if request.needs_csrf_check:
            # CSRF protection: Whenever a request has ambient authority (whether
            # through a session cookie or its origin IP address), it must include
            # the correct token, unless the HTTP method is GET or HEAD (and thus
            # guaranteed to be side effect free. In practice, the only situation
            # where we allow side effects without ambient authority is when the
            # authority comes from an API key; and that is handled above.
            from kallithea.lib import helpers as h
            token = request.POST.get(h.session_csrf_secret_name)
            if not token or token != h.session_csrf_secret_token():
                log.error('CSRF check failed')
                raise webob.exc.HTTPForbidden()

        c.kallithea_version = __version__
        rc_config = Setting.get_app_settings()

        # Visual options
        c.visual = AttributeDict({})

        ## DB stored
        c.visual.show_public_icon = str2bool(rc_config.get('show_public_icon'))
        c.visual.show_private_icon = str2bool(
            rc_config.get('show_private_icon'))
        c.visual.stylify_metalabels = str2bool(
            rc_config.get('stylify_metalabels'))
        c.visual.page_size = safe_int(rc_config.get('dashboard_items', 100))
        c.visual.admin_grid_items = safe_int(
            rc_config.get('admin_grid_items', 100))
        c.visual.repository_fields = str2bool(
            rc_config.get('repository_fields'))
        c.visual.show_version = str2bool(rc_config.get('show_version'))
        c.visual.use_gravatar = str2bool(rc_config.get('use_gravatar'))
        c.visual.gravatar_url = rc_config.get('gravatar_url')

        c.ga_code = rc_config.get('ga_code')
        # TODO: replace undocumented backwards compatibility hack with db upgrade and rename ga_code
        if c.ga_code and '<' not in c.ga_code:
            c.ga_code = '''<script type="text/javascript">
                var _gaq = _gaq || [];
                _gaq.push(['_setAccount', '%s']);
                _gaq.push(['_trackPageview']);

                (function() {
                    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
                    })();
            </script>''' % c.ga_code
        c.site_name = rc_config.get('title')
        c.clone_uri_tmpl = rc_config.get(
            'clone_uri_tmpl') or Repository.DEFAULT_CLONE_URI
        c.clone_ssh_tmpl = rc_config.get(
            'clone_ssh_tmpl') or Repository.DEFAULT_CLONE_SSH

        ## INI stored
        c.visual.allow_repo_location_change = str2bool(
            config.get('allow_repo_location_change', True))
        c.visual.allow_custom_hooks_settings = str2bool(
            config.get('allow_custom_hooks_settings', True))
        c.ssh_enabled = str2bool(config.get('ssh_enabled', False))

        c.instance_id = config.get('instance_id')
        c.issues_url = config.get('bugtracker', url('issues_url'))
        # END CONFIG VARS

        c.repo_name = get_repo_slug(request)  # can be empty
        c.backends = list(BACKENDS)

        self.cut_off_limit = safe_int(config.get('cut_off_limit'))

        c.my_pr_count = PullRequest.query(reviewer_id=request.authuser.user_id,
                                          include_closed=False).count()

        self.scm_model = ScmModel()
Esempio n. 30
0
def setup_configuration(app):
    config = app.config

    if not kallithea.lib.locale.current_locale_is_valid():
        log.error("Terminating ...")
        sys.exit(1)

    # Mercurial sets encoding at module import time, so we have to monkey patch it
    hgencoding = config.get('hgencoding')
    if hgencoding:
        mercurial.encoding.encoding = hgencoding

    if config.get('ignore_alembic_revision', False):
        log.warn('database alembic revision checking is disabled')
    else:
        dbconf = config['sqlalchemy.url']
        alembic_cfg = alembic.config.Config()
        alembic_cfg.set_main_option('script_location', 'kallithea:alembic')
        alembic_cfg.set_main_option('sqlalchemy.url', dbconf)
        script_dir = ScriptDirectory.from_config(alembic_cfg)
        available_heads = sorted(script_dir.get_heads())

        engine = create_engine(dbconf)
        with engine.connect() as conn:
            context = MigrationContext.configure(conn)
            current_heads = sorted(str(s) for s in context.get_current_heads())
        if current_heads != available_heads:
            log.error('Failed to run Kallithea:\n\n'
                      'The database version does not match the Kallithea version.\n'
                      'Please read the documentation on how to upgrade or downgrade the database.\n'
                      'Current database version id(s): %s\n'
                      'Expected database version id(s): %s\n'
                      'If you are a developer and you know what you are doing, you can add `ignore_alembic_revision = True` '
                      'to your .ini file to skip the check.\n' % (' '.join(current_heads), ' '.join(available_heads)))
            sys.exit(1)

    # store some globals into kallithea
    kallithea.DEFAULT_USER_ID = db.User.get_default_user().user_id

    if str2bool(config.get('use_celery')):
        kallithea.CELERY_APP = celerypylons.make_app()
    kallithea.CONFIG = config

    load_rcextensions(root_path=config['here'])

    set_app_settings(config)

    instance_id = kallithea.CONFIG.get('instance_id', '*')
    if instance_id == '*':
        instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
        kallithea.CONFIG['instance_id'] = instance_id

    # update kallithea.CONFIG with the meanwhile changed 'config'
    kallithea.CONFIG.update(config)

    # configure vcs and indexer libraries (they are supposed to be independent
    # as much as possible and thus avoid importing tg.config or
    # kallithea.CONFIG).
    set_vcs_config(kallithea.CONFIG)
    set_indexer_config(kallithea.CONFIG)

    check_git_version()

    kallithea.model.meta.Session.remove()
Esempio n. 31
0
def send_email(recipients,
               subject,
               body='',
               html_body='',
               headers=None,
               from_name=None):
    """
    Sends an email with defined parameters from the .ini files.

    :param recipients: list of recipients, if this is None, the defined email
        address from field 'email_to' and all admins is used instead
    :param subject: subject of the mail
    :param body: body of the mail
    :param html_body: html version of body
    :param headers: dictionary of prepopulated e-mail headers
    :param from_name: full name to be used as sender of this mail - often a
    .full_name_or_username value
    """
    assert isinstance(recipients, list), recipients
    if headers is None:
        headers = {}
    else:
        # do not modify the original headers object passed by the caller
        headers = headers.copy()

    email_config = config
    email_prefix = email_config.get('email_prefix', '')
    if email_prefix:
        subject = "%s %s" % (email_prefix, subject)

    if not recipients:
        # if recipients are not defined we send to email_config + all admins
        recipients = [
            u.email for u in User.query().filter(User.admin == True).all()
        ]
        if email_config.get('email_to') is not None:
            recipients += email_config.get('email_to').split(',')

        # If there are still no recipients, there are no admins and no address
        # configured in email_to, so return.
        if not recipients:
            log.error("No recipients specified and no fallback available.")
            return False

        log.warning("No recipients specified for '%s' - sending to admins %s",
                    subject, ' '.join(recipients))

    # SMTP sender
    envelope_from = email_config.get('app_email_from', 'Kallithea')
    # 'From' header
    if from_name is not None:
        # set From header based on from_name but with a generic e-mail address
        # In case app_email_from is in "Some Name <e-mail>" format, we first
        # extract the e-mail address.
        envelope_addr = author_email(envelope_from)
        headers['From'] = '"%s" <%s>' % (email.utils.quote(
            '%s (no-reply)' % from_name), envelope_addr)

    user = email_config.get('smtp_username')
    passwd = email_config.get('smtp_password')
    mail_server = email_config.get('smtp_server')
    mail_port = email_config.get('smtp_port')
    tls = str2bool(email_config.get('smtp_use_tls'))
    ssl = str2bool(email_config.get('smtp_use_ssl'))
    debug = str2bool(email_config.get('debug'))
    smtp_auth = email_config.get('smtp_auth')

    logmsg = ("Mail details:\n"
              "recipients: %s\n"
              "headers: %s\n"
              "subject: %s\n"
              "body:\n%s\n"
              "html:\n%s\n" %
              (' '.join(recipients), headers, subject, body, html_body))

    if mail_server:
        log.debug("Sending e-mail. " + logmsg)
    else:
        log.error("SMTP mail server not configured - cannot send e-mail.")
        log.warning(logmsg)
        return False

    try:
        m = SmtpMailer(envelope_from,
                       user,
                       passwd,
                       mail_server,
                       smtp_auth,
                       mail_port,
                       ssl,
                       tls,
                       debug=debug)
        m.send(recipients, subject, body, html_body, headers=headers)
    except:
        log.error('Mail sending failed')
        log.error(traceback.format_exc())
        return False
    return True
Esempio n. 32
0
 def custom_start_response(status, headers, exc_info=None):
     if is_ssl and str2bool(self.config.get('use_htsts')) and not debug:
         headers.append(('Strict-Transport-Security',
                         'max-age=8640000; includeSubDomains'))
     return start_response(status, headers, exc_info)
Esempio n. 33
0
        def to_python(self, value, state):
            perms_update = OrderedSet()
            perms_new = OrderedSet()
            # build a list of permission to update and new permission to create

            #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
            new_perms_group = defaultdict(dict)
            for k, v in value.copy().iteritems():
                if k.startswith('perm_new_member'):
                    del value[k]
                    _type, part = k.split('perm_new_member_')
                    args = part.split('_')
                    if len(args) == 1:
                        new_perms_group[args[0]]['perm'] = v
                    elif len(args) == 2:
                        _key, pos = args
                        new_perms_group[pos][_key] = v

            # fill new permissions in order of how they were added
            for k in sorted(map(int, new_perms_group.keys())):
                perm_dict = new_perms_group[str(k)]
                new_member = perm_dict.get('name')
                new_perm = perm_dict.get('perm')
                new_type = perm_dict.get('type')
                if new_member and new_perm and new_type:
                    perms_new.add((new_member, new_perm, new_type))

            for k, v in value.iteritems():
                if k.startswith('u_perm_') or k.startswith('g_perm_'):
                    member = k[7:]
                    t = {'u': 'user', 'g': 'users_group'}[k[0]]
                    if member == User.DEFAULT_USER:
                        if str2bool(value.get('repo_private')):
                            # set none for default when updating to
                            # private repo protects against form manipulation
                            v = EMPTY_PERM
                    perms_update.add((member, v, t))

            value['perms_updates'] = list(perms_update)
            value['perms_new'] = list(perms_new)

            # update permissions
            for k, v, t in perms_new:
                try:
                    if t is 'user':
                        self.user_db = User.query() \
                            .filter(User.active == True) \
                            .filter(User.username == k).one()
                    if t is 'users_group':
                        self.user_db = UserGroup.query() \
                            .filter(UserGroup.users_group_active == True) \
                            .filter(UserGroup.users_group_name == k).one()

                except Exception:
                    log.exception('Updated permission failed')
                    msg = self.message('perm_new_member_type', state)
                    raise formencode.Invalid(
                        msg,
                        value,
                        state,
                        error_dict=dict(perm_new_member_name=msg))
            return value
Esempio n. 34
0
def authenticate(username, password, environ=None):
    """
    Authentication function used for access control,
    It tries to authenticate based on enabled authentication modules.

    :param username: username can be empty for container auth
    :param password: password can be empty for container auth
    :param environ: environ headers passed for container auth
    :returns: None if auth failed, user_data dict if auth is correct
    """

    auth_plugins = Setting.get_auth_plugins()
    log.debug('Authentication against %s plugins', auth_plugins)
    for module in auth_plugins:
        try:
            plugin = loadplugin(module)
        except (ImportError, AttributeError, TypeError) as e:
            raise ImportError('Failed to load authentication module %s : %s'
                              % (module, str(e)))
        log.debug('Trying authentication using ** %s **', module)
        # load plugin settings from Kallithea database
        plugin_name = plugin.name
        plugin_settings = {}
        for v in plugin.plugin_settings():
            conf_key = "auth_%s_%s" % (plugin_name, v["name"])
            setting = Setting.get_by_name(conf_key)
            plugin_settings[v["name"]] = setting.app_settings_value if setting else None
        log.debug('Plugin settings \n%s', formatted_json(plugin_settings))

        if not str2bool(plugin_settings["enabled"]):
            log.info("Authentication plugin %s is disabled, skipping for %s",
                     module, username)
            continue

        # use plugin's method of user extraction.
        user = plugin.get_user(username, environ=environ,
                               settings=plugin_settings)
        log.debug('Plugin %s extracted user is `%s`', module, user)
        if not plugin.accepts(user):
            log.debug('Plugin %s does not accept user `%s` for authentication',
                      module, user)
            continue
        else:
            log.debug('Plugin %s accepted user `%s` for authentication',
                      module, user)
            # The user might have tried to authenticate using their email address,
            # then the username variable wouldn't contain a valid username.
            # But as the plugin has accepted the user, .username field should
            # have a valid username, so use it for authentication purposes.
            if user is not None:
                username = user.username

        log.info('Authenticating user using %s plugin', plugin.__module__)

        # _authenticate is a wrapper for .auth() method of plugin.
        # it checks if .auth() sends proper data. For KallitheaExternalAuthPlugin
        # it also maps users to Database and maps the attributes returned
        # from .auth() to Kallithea database. If this function returns data
        # then auth is correct.
        user_data = plugin._authenticate(user, username, password,
                                           plugin_settings,
                                           environ=environ or {})
        log.debug('PLUGIN USER DATA: %s', user_data)

        if user_data is not None:
            log.debug('Plugin returned proper authentication data')
            return user_data

        # we failed to Auth because .auth() method didn't return the user
        if username:
            log.warning("User `%s` failed to authenticate against %s",
                        username, plugin.__module__)
    return None
Esempio n. 35
0
def load_environment(global_conf,
                     app_conf,
                     initial=False,
                     test_env=None,
                     test_index=None):
    """
    Configure the Pylons environment via the ``pylons.config``
    object
    """
    config = PylonsConfig()

    # Pylons paths
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    paths = dict(root=root,
                 controllers=os.path.join(root, 'controllers'),
                 static_files=os.path.join(root, 'public'),
                 templates=[os.path.join(root, 'templates')])

    # Initialize config with the basic options
    config.init_app(global_conf, app_conf, package='kallithea', paths=paths)

    # store some globals into kallithea
    kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
    kallithea.CELERY_EAGER = str2bool(
        config['app_conf'].get('celery.always.eager'))

    config['routes.map'] = make_map(config)
    config['pylons.app_globals'] = app_globals.Globals(config)
    config['pylons.h'] = helpers
    kallithea.CONFIG = config

    load_rcextensions(root_path=config['here'])

    # Setup cache object as early as possible
    import pylons
    pylons.cache._push_object(config['pylons.app_globals'].cache)

    # Create the Mako TemplateLookup, with the default auto-escaping
    config['pylons.app_globals'].mako_lookup = TemplateLookup(
        directories=paths['templates'],
        error_handler=handle_mako_error,
        module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
        input_encoding='utf-8',
        default_filters=['escape'],
        imports=['from webhelpers.html import escape'])

    # sets the c attribute access when don't existing attribute are accessed
    config['pylons.strict_tmpl_context'] = True
    test = os.path.split(config['__file__'])[-1] == 'test.ini'
    if test:
        if test_env is None:
            test_env = not int(os.environ.get('KALLITHEA_NO_TMP_PATH', 0))
        if test_index is None:
            test_index = not int(
                os.environ.get('KALLITHEA_WHOOSH_TEST_DISABLE', 0))
        if os.environ.get('TEST_DB'):
            # swap config if we pass enviroment variable
            config['sqlalchemy.db1.url'] = os.environ.get('TEST_DB')

        from kallithea.lib.utils import create_test_env, create_test_index
        from kallithea.tests import TESTS_TMP_PATH
        #set KALLITHEA_NO_TMP_PATH=1 to disable re-creating the database and
        #test repos
        if test_env:
            create_test_env(TESTS_TMP_PATH, config)
        #set KALLITHEA_WHOOSH_TEST_DISABLE=1 to disable whoosh index during tests
        if test_index:
            create_test_index(TESTS_TMP_PATH, config, True)

    DbManage.check_waitress()
    # MULTIPLE DB configs
    # Setup the SQLAlchemy database engine
    sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
    init_model(sa_engine_db1)

    set_available_permissions(config)
    repos_path = make_ui('db').configitems('paths')[0][1]
    config['base_path'] = repos_path
    set_app_settings(config)

    instance_id = kallithea.CONFIG.get('instance_id')
    if instance_id == '*':
        instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
        kallithea.CONFIG['instance_id'] = instance_id

    # CONFIGURATION OPTIONS HERE (note: all config options will override
    # any Pylons config options)

    # store config reference into our module to skip import magic of
    # pylons
    kallithea.CONFIG.update(config)
    set_vcs_config(kallithea.CONFIG)

    #check git version
    check_git_version()

    if str2bool(config.get('initial_repo_scan', True)):
        repo2db_mapper(ScmModel().repo_scan(repos_path),
                       remove_obsolete=False,
                       install_git_hook=False)
    return config
Esempio n. 36
0
    def diff(self, repo_name, f_path):
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = request.GET.get('context', 3)
        diff2 = request.GET.get('diff2', '')
        diff1 = request.GET.get('diff1', '') or diff2
        c.action = request.GET.get('diff')
        c.no_changes = diff1 == diff2
        c.f_path = f_path
        c.big_diff = False
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.changes = OrderedDict()
        c.changes[diff2] = []

        #special case if we want a show rev only, it's impl here
        #to reduce JS and callbacks

        if request.GET.get('show_rev'):
            if str2bool(request.GET.get('annotate', 'False')):
                _url = url('files_annotate_home', repo_name=c.repo_name,
                           revision=diff1, f_path=c.f_path)
            else:
                _url = url('files_home', repo_name=c.repo_name,
                           revision=diff1, f_path=c.f_path)

            raise HTTPFound(location=_url)
        try:
            if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_1 = c.db_repo_scm_instance.get_changeset(diff1)
                try:
                    node1 = c.changeset_1.get_node(f_path)
                    if node1.is_dir():
                        raise NodeError('%s path is a %s not a file'
                                        % (node1, type(node1)))
                except NodeDoesNotExistError:
                    c.changeset_1 = EmptyChangeset(cs=diff1,
                                                   revision=c.changeset_1.revision,
                                                   repo=c.db_repo_scm_instance)
                    node1 = FileNode(f_path, '', changeset=c.changeset_1)
            else:
                c.changeset_1 = EmptyChangeset(repo=c.db_repo_scm_instance)
                node1 = FileNode(f_path, '', changeset=c.changeset_1)

            if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_2 = c.db_repo_scm_instance.get_changeset(diff2)
                try:
                    node2 = c.changeset_2.get_node(f_path)
                    if node2.is_dir():
                        raise NodeError('%s path is a %s not a file'
                                        % (node2, type(node2)))
                except NodeDoesNotExistError:
                    c.changeset_2 = EmptyChangeset(cs=diff2,
                                                   revision=c.changeset_2.revision,
                                                   repo=c.db_repo_scm_instance)
                    node2 = FileNode(f_path, '', changeset=c.changeset_2)
            else:
                c.changeset_2 = EmptyChangeset(repo=c.db_repo_scm_instance)
                node2 = FileNode(f_path, '', changeset=c.changeset_2)
        except (RepositoryError, NodeError):
            log.error(traceback.format_exc())
            raise HTTPFound(location=url('files_home', repo_name=c.repo_name,
                                f_path=f_path))

        if c.action == 'download':
            _diff = diffs.get_gitdiff(node1, node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')

            diff_name = '%s_vs_%s.diff' % (diff1, diff2)
            response.content_type = 'text/plain'
            response.content_disposition = (
                'attachment; filename=%s' % diff_name
            )
            return diff.as_raw()

        elif c.action == 'raw':
            _diff = diffs.get_gitdiff(node1, node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')
            response.content_type = 'text/plain'
            return diff.as_raw()

        else:
            fid = h.FID(diff2, node2.path)
            line_context_lcl = get_line_ctx(fid, request.GET)
            ign_whitespace_lcl = get_ignore_ws(fid, request.GET)

            lim = request.GET.get('fulldiff') or self.cut_off_limit
            _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
                                         filenode_new=node2,
                                         cut_off_limit=lim,
                                         ignore_whitespace=ign_whitespace_lcl,
                                         line_context=line_context_lcl,
                                         enable_comments=False)
            op = ''
            filename = node1.path
            cs_changes = {
                'fid': [cs1, cs2, op, filename, diff, st]
            }
            c.changes = cs_changes

        return render('files/file_diff.html')
Esempio n. 37
0
 def test_str2bool(self, str_bool, expected):
     from kallithea.lib.utils2 import str2bool
     assert str2bool(str_bool) == expected
Esempio n. 38
0
def authenticate(username, password, environ=None):
    """
    Authentication function used for access control,
    It tries to authenticate based on enabled authentication modules.

    :param username: username can be empty for container auth
    :param password: password can be empty for container auth
    :param environ: environ headers passed for container auth
    :returns: None if auth failed, plugin_user dict if auth is correct
    """

    auth_plugins = Setting.get_auth_plugins()
    log.debug('Authentication against %s plugins' % (auth_plugins, ))
    for module in auth_plugins:
        try:
            plugin = loadplugin(module)
        except (ImportError, AttributeError, TypeError), e:
            raise ImportError('Failed to load authentication module %s : %s' %
                              (module, str(e)))
        log.debug('Trying authentication using ** %s **' % (module, ))
        # load plugin settings from Kallithea database
        plugin_name = plugin.name
        plugin_settings = {}
        for v in plugin.plugin_settings():
            conf_key = "auth_%s_%s" % (plugin_name, v["name"])
            setting = Setting.get_by_name(conf_key)
            plugin_settings[
                v["name"]] = setting.app_settings_value if setting else None
        log.debug('Plugin settings \n%s' % formatted_json(plugin_settings))

        if not str2bool(plugin_settings["enabled"]):
            log.info("Authentication plugin %s is disabled, skipping for %s" %
                     (module, username))
            continue

        # use plugin's method of user extraction.
        user = plugin.get_user(username,
                               environ=environ,
                               settings=plugin_settings)
        log.debug('Plugin %s extracted user is `%s`' % (module, user))
        if not plugin.accepts(user):
            log.debug(
                'Plugin %s does not accept user `%s` for authentication' %
                (module, user))
            continue
        else:
            log.debug('Plugin %s accepted user `%s` for authentication' %
                      (module, user))

        log.info('Authenticating user using %s plugin' % plugin.__module__)
        # _authenticate is a wrapper for .auth() method of plugin.
        # it checks if .auth() sends proper data. For KallitheaExternalAuthPlugin
        # it also maps users to Database and maps the attributes returned
        # from .auth() to Kallithea database. If this function returns data
        # then auth is correct.
        plugin_user = plugin._authenticate(user,
                                           username,
                                           password,
                                           plugin_settings,
                                           environ=environ or {})
        log.debug('PLUGIN USER DATA: %s' % plugin_user)

        if plugin_user:
            log.debug('Plugin returned proper authentication data')
            return plugin_user

        # we failed to Auth because .auth() method didn't return proper the user
        if username:
            log.warning("User `%s` failed to authenticate against %s" %
                        (username, plugin.__module__))
Esempio n. 39
0
        def to_python(self, value, state):
            perms_update = OrderedSet()
            perms_new = OrderedSet()
            # build a list of permission to update and new permission to create

            #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
            new_perms_group = defaultdict(dict)
            for k, v in value.copy().iteritems():
                if k.startswith('perm_new_member'):
                    del value[k]
                    _type, part = k.split('perm_new_member_')
                    args = part.split('_')
                    if len(args) == 1:
                        new_perms_group[args[0]]['perm'] = v
                    elif len(args) == 2:
                        _key, pos = args
                        new_perms_group[pos][_key] = v

            # fill new permissions in order of how they were added
            for k in sorted(map(int, new_perms_group.keys())):
                perm_dict = new_perms_group[str(k)]
                new_member = perm_dict.get('name')
                new_perm = perm_dict.get('perm')
                new_type = perm_dict.get('type')
                if new_member and new_perm and new_type:
                    perms_new.add((new_member, new_perm, new_type))

            for k, v in value.iteritems():
                if k.startswith('u_perm_') or k.startswith('g_perm_'):
                    member = k[7:]
                    t = {'u': 'user',
                         'g': 'users_group'
                    }[k[0]]
                    if member == User.DEFAULT_USER:
                        if str2bool(value.get('repo_private')):
                            # set none for default when updating to
                            # private repo protects against form manipulation
                            v = EMPTY_PERM
                    perms_update.add((member, v, t))

            value['perms_updates'] = list(perms_update)
            value['perms_new'] = list(perms_new)

            # update permissions
            for k, v, t in perms_new:
                try:
                    if t is 'user':
                        self.user_db = User.query() \
                            .filter(User.active == True) \
                            .filter(User.username == k).one()
                    if t is 'users_group':
                        self.user_db = UserGroup.query() \
                            .filter(UserGroup.users_group_active == True) \
                            .filter(UserGroup.users_group_name == k).one()

                except Exception:
                    log.exception('Updated permission failed')
                    msg = M(self, 'perm_new_member_type', state)
                    raise formencode.Invalid(msg, value, state,
                        error_dict=dict(perm_new_member_name=msg)
                    )
            return value
Esempio n. 40
0
 def test_str2bool(self, str_bool, expected):
     from kallithea.lib.utils2 import str2bool
     self.assertEqual(str2bool(str_bool), expected)
Esempio n. 41
0
    def diff(self, repo_name, f_path):
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = safe_int(request.GET.get('context'), 3)
        diff2 = request.GET.get('diff2', '')
        diff1 = request.GET.get('diff1', '') or diff2
        c.action = request.GET.get('diff')
        c.no_changes = diff1 == diff2
        c.f_path = f_path
        c.big_diff = False
        fulldiff = request.GET.get('fulldiff')
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.changes = OrderedDict()
        c.changes[diff2] = []

        # special case if we want a show rev only, it's impl here
        # to reduce JS and callbacks

        if request.GET.get('show_rev'):
            if str2bool(request.GET.get('annotate', 'False')):
                _url = url('files_annotate_home',
                           repo_name=c.repo_name,
                           revision=diff1,
                           f_path=c.f_path)
            else:
                _url = url('files_home',
                           repo_name=c.repo_name,
                           revision=diff1,
                           f_path=c.f_path)

            raise HTTPFound(location=_url)
        try:
            if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_1 = c.db_repo_scm_instance.get_changeset(diff1)
                try:
                    node1 = c.changeset_1.get_node(f_path)
                    if node1.is_dir():
                        raise NodeError('%s path is a %s not a file' %
                                        (node1, type(node1)))
                except NodeDoesNotExistError:
                    c.changeset_1 = EmptyChangeset(
                        cs=diff1,
                        revision=c.changeset_1.revision,
                        repo=c.db_repo_scm_instance)
                    node1 = FileNode(f_path, '', changeset=c.changeset_1)
            else:
                c.changeset_1 = EmptyChangeset(repo=c.db_repo_scm_instance)
                node1 = FileNode(f_path, '', changeset=c.changeset_1)

            if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_2 = c.db_repo_scm_instance.get_changeset(diff2)
                try:
                    node2 = c.changeset_2.get_node(f_path)
                    if node2.is_dir():
                        raise NodeError('%s path is a %s not a file' %
                                        (node2, type(node2)))
                except NodeDoesNotExistError:
                    c.changeset_2 = EmptyChangeset(
                        cs=diff2,
                        revision=c.changeset_2.revision,
                        repo=c.db_repo_scm_instance)
                    node2 = FileNode(f_path, '', changeset=c.changeset_2)
            else:
                c.changeset_2 = EmptyChangeset(repo=c.db_repo_scm_instance)
                node2 = FileNode(f_path, '', changeset=c.changeset_2)
        except (RepositoryError, NodeError):
            log.error(traceback.format_exc())
            raise HTTPFound(location=url(
                'files_home', repo_name=c.repo_name, f_path=f_path))

        if c.action == 'download':
            raw_diff = diffs.get_gitdiff(node1,
                                         node2,
                                         ignore_whitespace=ignore_whitespace,
                                         context=line_context)
            diff_name = '%s_vs_%s.diff' % (diff1, diff2)
            response.content_type = 'text/plain'
            response.content_disposition = ('attachment; filename=%s' %
                                            diff_name)
            return raw_diff

        elif c.action == 'raw':
            raw_diff = diffs.get_gitdiff(node1,
                                         node2,
                                         ignore_whitespace=ignore_whitespace,
                                         context=line_context)
            response.content_type = 'text/plain'
            return raw_diff

        else:
            fid = h.FID(diff2, node2.path)
            line_context_lcl = get_line_ctx(fid, request.GET)
            ign_whitespace_lcl = get_ignore_ws(fid, request.GET)

            diff_limit = None if fulldiff else self.cut_off_limit
            c.a_rev, c.cs_rev, a_path, diff, st, op = diffs.wrapped_diff(
                filenode_old=node1,
                filenode_new=node2,
                diff_limit=diff_limit,
                ignore_whitespace=ign_whitespace_lcl,
                line_context=line_context_lcl,
                enable_comments=False)
            c.file_diff_data = [(fid, fid, op, a_path, node2.path, diff, st)]

            return render('files/file_diff.html')