def auth(self, userobj, username, password, settings, **kwargs): """ Given a user object (which may be null), username, a plaintext password, and a settings object (containing all the keys needed as listed in settings()), authenticate this user's login attempt. Return None on failure. On success, return a dictionary of the form: see: KallitheaAuthPluginBase.auth_func_attrs This is later validated for correctness """ if not username or not password: log.debug('Empty username or password skipping...') return None log.debug("Crowd settings: \n%s" % (formatted_json(settings))) server = CrowdServer(**settings) server.set_credentials(settings["app_name"], settings["app_password"]) crowd_user = server.user_auth(username, password) log.debug("Crowd returned: \n%s" % (formatted_json(crowd_user))) if not crowd_user["status"]: return None res = server.user_groups(crowd_user["name"]) log.debug("Crowd groups: \n%s" % (formatted_json(res))) crowd_user["groups"] = [x["name"] for x in res["groups"]] # old attrs fetched from Kallithea database admin = getattr(userobj, 'admin', False) active = getattr(userobj, 'active', True) email = getattr(userobj, 'email', '') firstname = getattr(userobj, 'firstname', '') lastname = getattr(userobj, 'lastname', '') extern_type = getattr(userobj, 'extern_type', '') user_attrs = { 'username': username, 'firstname': crowd_user["first-name"] or firstname, 'lastname': crowd_user["last-name"] or lastname, 'groups': crowd_user["groups"], 'email': crowd_user["email"] or email, 'admin': admin, 'active': active, 'active_from_extern': crowd_user.get('active'), 'extern_name': crowd_user["name"], 'extern_type': extern_type, } # set an admin if we're in admin_groups of crowd for group in settings["admin_groups"].split(","): if group in user_attrs["groups"]: user_attrs["admin"] = True log.debug("Final crowd user object: \n%s" % (formatted_json(user_attrs))) log.info('user %s authenticated correctly' % user_attrs['username']) return user_attrs
def auth(self, userobj, username, password, settings, **kwargs): """ Given a user object (which may be null), username, a plaintext password, and a settings object (containing all the keys needed as listed in settings()), authenticate this user's login attempt. Return None on failure. On success, return a dictionary of the form: see: KallitheaAuthPluginBase.auth_func_attrs This is later validated for correctness """ if not username or not password: log.debug('Empty username or password skipping...') return None log.debug("Crowd settings: \n%s", formatted_json(settings)) server = CrowdServer(**settings) server.set_credentials(settings["app_name"], settings["app_password"]) crowd_user = server.user_auth(username, password) log.debug("Crowd returned: \n%s", formatted_json(crowd_user)) if not crowd_user["status"]: return None res = server.user_groups(crowd_user["name"]) log.debug("Crowd groups: \n%s", formatted_json(res)) crowd_user["groups"] = [x["name"] for x in res["groups"]] # old attrs fetched from Kallithea database admin = getattr(userobj, 'admin', False) active = getattr(userobj, 'active', True) email = getattr(userobj, 'email', '') firstname = getattr(userobj, 'firstname', '') lastname = getattr(userobj, 'lastname', '') user_data = { 'username': crowd_user["name"] or username, 'firstname': crowd_user["first-name"] or firstname, 'lastname': crowd_user["last-name"] or lastname, 'groups': crowd_user["groups"], 'email': crowd_user["email"] or email, 'admin': admin, 'active': active, 'active_from_extern': crowd_user.get('active'), # ??? 'extern_name': crowd_user["name"], } # set an admin if we're in admin_groups of crowd for group in settings["admin_groups"].split(","): if group in user_data["groups"]: user_data["admin"] = True log.debug("Final crowd user object: \n%s", formatted_json(user_data)) log.info('user %s authenticated correctly', user_data['username']) return user_data
def auth_settings(self): """POST create and store auth settings""" self.__load_defaults() _form = AuthSettingsForm(c.enabled_plugins)() log.debug("POST Result: %s" % formatted_json(dict(request.POST))) try: form_result = _form.to_python(dict(request.POST)) for k, v in form_result.items(): if k == 'auth_plugins': # we want to store it comma separated inside our settings v = ','.join(v) log.debug("%s = %s" % (k, str(v))) setting = Setting.create_or_update(k, v) Session().add(setting) Session().commit() h.flash(_('Auth settings updated successfully'), category='success') except formencode.Invalid, errors: log.error(traceback.format_exc()) e = errors.error_dict or {} return self.index( defaults=errors.value, errors=e, prefix_error=False)
def __render(self, defaults, errors): c.defaults = {} c.plugin_settings = {} c.plugin_shortnames = {} for plugin in self.enabled_plugins: module = plugin.__class__.__module__ c.plugin_shortnames[module] = plugin.name c.plugin_settings[module] = plugin.plugin_settings() for v in c.plugin_settings[module]: fullname = "auth_%s_%s" % (plugin.name, v["name"]) if "default" in v: c.defaults[fullname] = v["default"] # Current values will be the default on the form, if there are any setting = Setting.get_by_name(fullname) if setting is not None: c.defaults[fullname] = setting.app_settings_value # we want to show , separated list of enabled plugins c.defaults['auth_plugins'] = ','.join(c.enabled_plugin_names) if defaults: c.defaults.update(defaults) log.debug(formatted_json(defaults)) return formencode.htmlfill.render( render('admin/auth/auth_settings.html'), defaults=c.defaults, errors=errors, prefix_error=False, encoding="UTF-8", force_defaults=False)
def __render(self, defaults, errors): c.defaults = {} c.plugin_settings = {} c.plugin_shortnames = {} for plugin in self.enabled_plugins: module = plugin.__class__.__module__ c.plugin_shortnames[module] = plugin.name c.plugin_settings[module] = plugin.plugin_settings() for v in c.plugin_settings[module]: fullname = "auth_%s_%s" % (plugin.name, v["name"]) if "default" in v: c.defaults[fullname] = v["default"] # Current values will be the default on the form, if there are any setting = Setting.get_by_name(fullname) if setting is not None: c.defaults[fullname] = setting.app_settings_value # we want to show , separated list of enabled plugins c.defaults['auth_plugins'] = ','.join(c.enabled_plugin_names) if defaults: c.defaults.update(defaults) log.debug(formatted_json(defaults)) return formencode.htmlfill.render( render('admin/auth/auth_settings.html'), defaults=c.defaults, errors=errors, prefix_error=False, encoding="UTF-8", force_defaults=False)
def auth(self, userobj, username, password, settings, **kwargs): if username not in _auth_cache: # Need lock here, as PAM authentication is not thread safe _pam_lock.acquire() try: auth_result = pam.authenticate(username, password, settings["service"]) # cache result only if we properly authenticated if auth_result: _auth_cache[username] = time.time() finally: _pam_lock.release() if not auth_result: log.error("PAM was unable to authenticate user: %s" % (username, )) return None else: log.debug("Using cached auth for user: %s" % (username, )) # old attrs fetched from Kallithea database admin = getattr(userobj, 'admin', False) active = getattr(userobj, 'active', True) email = getattr(userobj, 'email', '') or "%s@%s" % (username, socket.gethostname()) firstname = getattr(userobj, 'firstname', '') lastname = getattr(userobj, 'lastname', '') extern_type = getattr(userobj, 'extern_type', '') user_attrs = { 'username': username, 'firstname': firstname, 'lastname': lastname, 'groups': [g.gr_name for g in grp.getgrall() if username in g.gr_mem], 'email': email, 'admin': admin, 'active': active, "active_from_extern": None, 'extern_name': username, 'extern_type': extern_type, } try: user_data = pwd.getpwnam(username) regex = settings["gecos"] match = re.search(regex, user_data.pw_gecos) if match: user_attrs["firstname"] = match.group('first_name') user_attrs["lastname"] = match.group('last_name') except Exception: log.warning("Cannot extract additional info for PAM user %s", username) pass log.debug("pamuser: \n%s" % formatted_json(user_attrs)) log.info('user %s authenticated correctly' % user_attrs['username']) return user_attrs
def auth(self, userobj, username, password, settings, **kwargs): if not username: log.debug('Empty username - skipping...') return None if username not in _auth_cache: # Need lock here, as PAM authentication is not thread safe _pam_lock.acquire() try: auth_result = pam_authenticate(username, password, settings["service"]) # cache result only if we properly authenticated if auth_result: _auth_cache[username] = time.time() finally: _pam_lock.release() if not auth_result: log.error("PAM was unable to authenticate user: %s", username) return None else: log.debug("Using cached auth for user: %s", username) # old attrs fetched from Kallithea database admin = getattr(userobj, 'admin', False) active = getattr(userobj, 'active', True) email = getattr(userobj, 'email', '') or "%s@%s" % (username, socket.gethostname()) firstname = getattr(userobj, 'firstname', '') lastname = getattr(userobj, 'lastname', '') user_data = { 'username': username, 'firstname': firstname, 'lastname': lastname, 'groups': [g.gr_name for g in grp.getgrall() if username in g.gr_mem], 'email': email, 'admin': admin, 'active': active, "active_from_extern": None, 'extern_name': username, } try: user_pw_data = pwd.getpwnam(username) regex = settings["gecos"] match = re.search(regex, user_pw_data.pw_gecos) if match: user_data["firstname"] = match.group('first_name') user_data["lastname"] = match.group('last_name') except Exception: log.warning("Cannot extract additional info for PAM user %s", username) pass log.debug("pamuser: \n%s", formatted_json(user_data)) log.info('user %s authenticated correctly', user_data['username']) return user_data
def auth_settings(self): """POST create and store auth settings""" self.__load_defaults() log.debug("POST Result: %s", formatted_json(dict(request.POST))) # First, parse only the plugin list (not the plugin settings). _auth_plugins_validator = AuthSettingsForm([]).fields['auth_plugins'] try: new_enabled_plugins = _auth_plugins_validator.to_python( request.POST.get('auth_plugins')) except formencode.Invalid: # User provided an invalid plugin list. Just fall back to # the list of currently enabled plugins. (We'll re-validate # and show an error message to the user, below.) pass else: # Hide plugins that the user has asked to be disabled, but # do not show plugins that the user has asked to be enabled # (yet), since that'll cause validation errors and/or wrong # settings being applied (e.g. checkboxes being cleared), # since the plugin settings will not be in the POST data. c.enabled_plugin_names = [ p for p in c.enabled_plugin_names if p in new_enabled_plugins ] # Next, parse everything including plugin settings. _form = AuthSettingsForm(c.enabled_plugin_names)() try: form_result = _form.to_python(dict(request.POST)) for k, v in form_result.items(): if k == 'auth_plugins': # we want to store it comma separated inside our settings v = ','.join(v) log.debug("%s = %s", k, str(v)) setting = Setting.create_or_update(k, v) Session().commit() h.flash(_('Auth settings updated successfully'), category='success') except formencode.Invalid as errors: log.error(traceback.format_exc()) e = errors.error_dict or {} return self.__render( defaults=errors.value, errors=e, ) except Exception: log.error(traceback.format_exc()) h.flash(_('error occurred during update of auth settings'), category='error') raise HTTPFound(location=url('auth_home'))
def auth_settings(self): """POST create and store auth settings""" self.__load_defaults() log.debug("POST Result: %s", formatted_json(dict(request.POST))) # First, parse only the plugin list (not the plugin settings). _auth_plugins_validator = AuthSettingsForm([]).fields['auth_plugins'] try: new_enabled_plugins = _auth_plugins_validator.to_python(request.POST.get('auth_plugins')) except formencode.Invalid: # User provided an invalid plugin list. Just fall back to # the list of currently enabled plugins. (We'll re-validate # and show an error message to the user, below.) pass else: # Hide plugins that the user has asked to be disabled, but # do not show plugins that the user has asked to be enabled # (yet), since that'll cause validation errors and/or wrong # settings being applied (e.g. checkboxes being cleared), # since the plugin settings will not be in the POST data. c.enabled_plugins = [ p for p in c.enabled_plugins if p in new_enabled_plugins ] # Next, parse everything including plugin settings. _form = AuthSettingsForm(c.enabled_plugins)() try: form_result = _form.to_python(dict(request.POST)) for k, v in form_result.items(): if k == 'auth_plugins': # we want to store it comma separated inside our settings v = ','.join(v) log.debug("%s = %s", k, str(v)) setting = Setting.create_or_update(k, v) Session().add(setting) Session().commit() h.flash(_('Auth settings updated successfully'), category='success') except formencode.Invalid as errors: log.error(traceback.format_exc()) e = errors.error_dict or {} return self.__render( defaults=errors.value, errors=e, ) except Exception: log.error(traceback.format_exc()) h.flash(_('error occurred during update of auth settings'), category='error') raise HTTPFound(location=url('auth_home'))
def _request(self, url, body=None, headers=None, method=None, noformat=False, empty_response_ok=False): _headers = { "Content-type": "application/json", "Accept": "application/json" } if self.user and self.passwd: authstring = base64.b64encode("%s:%s" % (self.user, self.passwd)) _headers["Authorization"] = "Basic %s" % authstring if headers: _headers.update(headers) log.debug("Sent crowd: \n%s" % (formatted_json({ "url": url, "body": body, "headers": _headers }))) request = urllib2.Request(url, body, _headers) if method: request.get_method = lambda: method global msg msg = "" try: rdoc = self.opener.open(request) msg = "".join(rdoc.readlines()) if not msg and empty_response_ok: rval = {} rval["status"] = True rval["error"] = "Response body was empty" elif not noformat: rval = json.loads(msg) rval["status"] = True else: rval = "".join(rdoc.readlines()) except Exception, e: if not noformat: rval = { "status": False, "body": body, "error": str(e) + "\n" + msg } else: rval = None
def index(self, defaults=None, errors=None, prefix_error=False): self.__load_defaults() _defaults = {} # default plugins loaded formglobals = { "auth_plugins": ["kallithea.lib.auth_modules.auth_internal"] } formglobals.update(Setting.get_auth_settings()) formglobals["plugin_settings"] = {} formglobals["auth_plugins_shortnames"] = {} _defaults["auth_plugins"] = formglobals["auth_plugins"] for module in formglobals["auth_plugins"]: plugin = auth_modules.loadplugin(module) plugin_name = plugin.name formglobals["auth_plugins_shortnames"][module] = plugin_name formglobals["plugin_settings"][module] = plugin.plugin_settings() for v in formglobals["plugin_settings"][module]: fullname = ("auth_" + plugin_name + "_" + v["name"]) if "default" in v: _defaults[fullname] = v["default"] # Current values will be the default on the form, if there are any setting = Setting.get_by_name(fullname) if setting: _defaults[fullname] = setting.app_settings_value # we want to show , separated list of enabled plugins _defaults['auth_plugins'] = ','.join(_defaults['auth_plugins']) if defaults: _defaults.update(defaults) formglobals["defaults"] = _defaults # set template context variables for k, v in formglobals.iteritems(): setattr(c, k, v) log.debug(pprint.pformat(formglobals, indent=4)) log.debug(formatted_json(defaults)) return formencode.htmlfill.render( render('admin/auth/auth_settings.html'), defaults=_defaults, errors=errors, prefix_error=prefix_error, encoding="UTF-8", force_defaults=False)
def index(self, defaults=None, errors=None, prefix_error=False): self.__load_defaults() _defaults = {} # default plugins loaded formglobals = { "auth_plugins": ["kallithea.lib.auth_modules.auth_internal"] } formglobals.update(Setting.get_auth_settings()) formglobals["plugin_settings"] = {} formglobals["auth_plugins_shortnames"] = {} _defaults["auth_plugins"] = formglobals["auth_plugins"] for module in formglobals["auth_plugins"]: plugin = auth_modules.loadplugin(module) plugin_name = plugin.name formglobals["auth_plugins_shortnames"][module] = plugin_name formglobals["plugin_settings"][module] = plugin.plugin_settings() for v in formglobals["plugin_settings"][module]: fullname = ("auth_" + plugin_name + "_" + v["name"]) if "default" in v: _defaults[fullname] = v["default"] # Current values will be the default on the form, if there are any setting = Setting.get_by_name(fullname) if setting: _defaults[fullname] = setting.app_settings_value # we want to show , separated list of enabled plugins _defaults['auth_plugins'] = ','.join(_defaults['auth_plugins']) if defaults: _defaults.update(defaults) formglobals["defaults"] = _defaults # set template context variables for k, v in formglobals.iteritems(): setattr(c, k, v) log.debug(pprint.pformat(formglobals, indent=4)) log.debug(formatted_json(defaults)) return formencode.htmlfill.render( render('admin/auth/auth_settings.html'), defaults=_defaults, errors=errors, prefix_error=prefix_error, encoding="UTF-8", force_defaults=False)
def auth(self, userobj, username, password, settings, **kwargs): if not userobj: log.debug('userobj was:%s skipping', userobj) return None if userobj.extern_type != self.name: log.warning( "userobj:%s extern_type mismatch got:`%s` expected:`%s`", userobj, userobj.extern_type, self.name) return None if not username: log.debug('Empty username - skipping...') return None user_data = { "username": userobj.username, "firstname": userobj.firstname, "lastname": userobj.lastname, "groups": [], "email": userobj.email, "admin": userobj.admin, "active": userobj.active, "active_from_extern": userobj.active, "extern_name": userobj.user_id, } log.debug(formatted_json(user_data)) if userobj.active: from kallithea.lib import auth password_match = auth.check_password(password, userobj.password) if userobj.is_default_user and userobj.active: log.info('user %s authenticated correctly as anonymous user', username) return user_data elif userobj.username == username and password_match: log.info('user %s authenticated correctly', user_data['username']) return user_data log.error("user %s had a bad password", username) return None else: log.warning('user %s tried auth but is disabled', username) return None
def auth(self, userobj, username, password, settings, **kwargs): if not userobj: log.debug('userobj was:%s skipping', userobj) return None if userobj.extern_type != self.name: log.warning("userobj:%s extern_type mismatch got:`%s` expected:`%s`", userobj, userobj.extern_type, self.name) return None if not username: log.debug('Empty username - skipping...') return None user_data = { "username": userobj.username, "firstname": userobj.firstname, "lastname": userobj.lastname, "groups": [], "email": userobj.email, "admin": userobj.admin, "active": userobj.active, "active_from_extern": userobj.active, "extern_name": userobj.user_id, } log.debug(formatted_json(user_data)) if userobj.active: from kallithea.lib import auth password_match = auth.KallitheaCrypto.hash_check(password, userobj.password) if userobj.username == User.DEFAULT_USER and userobj.active: log.info('user %s authenticated correctly as anonymous user', username) return user_data elif userobj.username == username and password_match: log.info('user %s authenticated correctly', user_data['username']) return user_data log.error("user %s had a bad password", username) return None else: log.warning('user %s tried auth but is disabled', username) return None
def auth(self, userobj, username, password, settings, **kwargs): if not userobj: log.debug('userobj was:%s skipping' % (userobj, )) return None if userobj.extern_type != self.name: log.warning("userobj:%s extern_type mismatch got:`%s` expected:`%s`" % (userobj, userobj.extern_type, self.name)) return None user_attrs = { "username": userobj.username, "firstname": userobj.firstname, "lastname": userobj.lastname, "groups": [], "email": userobj.email, "admin": userobj.admin, "active": userobj.active, "active_from_extern": userobj.active, "extern_name": userobj.user_id, 'extern_type': userobj.extern_type, } log.debug(formatted_json(user_attrs)) if userobj.active: from kallithea.lib import auth password_match = auth.KallitheaCrypto.hash_check(password, userobj.password) if userobj.username == User.DEFAULT_USER and userobj.active: log.info('user %s authenticated correctly as anonymous user' % username) return user_attrs elif userobj.username == username and password_match: log.info('user %s authenticated correctly' % user_attrs['username']) return user_attrs log.error("user %s had a bad password" % username) return None else: log.warning('user %s tried auth but is disabled' % username) return None
def _request(self, url, body=None, headers=None, method=None, noformat=False, empty_response_ok=False): _headers = {"Content-type": "application/json", "Accept": "application/json"} if self.user and self.passwd: authstring = base64.b64encode("%s:%s" % (self.user, self.passwd)) _headers["Authorization"] = "Basic %s" % authstring if headers: _headers.update(headers) log.debug("Sent crowd: \n%s", formatted_json({"url": url, "body": body, "headers": _headers})) request = urllib2.Request(url, body, _headers) if method: request.get_method = lambda: method global msg msg = "" try: rdoc = self.opener.open(request) msg = "".join(rdoc.readlines()) if not msg and empty_response_ok: rval = {} rval["status"] = True rval["error"] = "Response body was empty" elif not noformat: rval = json.loads(msg) rval["status"] = True else: rval = "".join(rdoc.readlines()) except Exception as e: if not noformat: rval = {"status": False, "body": body, "error": str(e) + "\n" + msg} else: rval = None return rval
def auth_settings(self): """POST create and store auth settings""" self.__load_defaults() _form = AuthSettingsForm(c.enabled_plugins)() log.debug("POST Result: %s" % formatted_json(dict(request.POST))) try: form_result = _form.to_python(dict(request.POST)) for k, v in form_result.items(): if k == 'auth_plugins': # we want to store it comma separated inside our settings v = ','.join(v) log.debug("%s = %s" % (k, str(v))) setting = Setting.create_or_update(k, v) Session().add(setting) Session().commit() h.flash(_('Auth settings updated successfully'), category='success') except formencode.Invalid, errors: log.error(traceback.format_exc()) e = errors.error_dict or {} return self.index(defaults=errors.value, errors=e, prefix_error=False)
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 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 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