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)
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()
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)
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)
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)
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)
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
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))
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))
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
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))
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))
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
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()
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
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)
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
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()
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)
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)
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
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
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
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
def app_settings_value(self): v = self._app_settings_value if self.app_settings_name == 'ldap_active': v = str2bool(v) return v
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
def test_str2bool(self, str_bool, expected): from kallithea.lib.utils2 import str2bool assert str2bool(str_bool) == expected
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
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()
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()
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
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)
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
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
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
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')
def test_str2bool(self, str_bool, expected): from kallithea.lib.utils2 import str2bool assert str2bool(str_bool) == expected
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__))
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
def test_str2bool(self, str_bool, expected): from kallithea.lib.utils2 import str2bool self.assertEqual(str2bool(str_bool), expected)
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')