diff --git a/rhodecode/authentication/base.py b/rhodecode/authentication/base.py --- a/rhodecode/authentication/base.py +++ b/rhodecode/authentication/base.py @@ -627,3 +627,21 @@ def authenticate(username, password, env log.debug("User `%s` failed to authenticate against %s", display_user, plugin.get_id()) return None + + +def chop_at(s, sub, inclusive=False): + """Truncate string ``s`` at the first occurrence of ``sub``. + + If ``inclusive`` is true, truncate just after ``sub`` rather than at it. + + >>> chop_at("plutocratic brats", "rat") + 'plutoc' + >>> chop_at("plutocratic brats", "rat", True) + 'plutocrat' + """ + pos = s.find(sub) + if pos == -1: + return s + if inclusive: + return s[:pos+len(sub)] + return s[:pos] diff --git a/rhodecode/authentication/plugins/auth_crowd.py b/rhodecode/authentication/plugins/auth_crowd.py --- a/rhodecode/authentication/plugins/auth_crowd.py +++ b/rhodecode/authentication/plugins/auth_crowd.py @@ -28,10 +28,9 @@ import base64 import logging import urllib2 -from pylons.i18n.translation import lazy_ugettext as _ -from sqlalchemy.ext.hybrid import hybrid_property - -from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin +from rhodecode.translation import _ +from rhodecode.authentication.base import ( + RhodeCodeExternalAuthPlugin, hybrid_property) from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase from rhodecode.authentication.routes import AuthnPluginResourceBase from rhodecode.lib.colander_utils import strip_whitespace diff --git a/rhodecode/authentication/plugins/auth_headers.py b/rhodecode/authentication/plugins/auth_headers.py --- a/rhodecode/authentication/plugins/auth_headers.py +++ b/rhodecode/authentication/plugins/auth_headers.py @@ -21,15 +21,14 @@ import colander import logging -from sqlalchemy.ext.hybrid import hybrid_property - -from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin +from rhodecode.translation import _ +from rhodecode.authentication.base import ( + RhodeCodeExternalAuthPlugin, hybrid_property) from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase from rhodecode.authentication.routes import AuthnPluginResourceBase from rhodecode.lib.colander_utils import strip_whitespace from rhodecode.lib.utils2 import str2bool, safe_unicode from rhodecode.model.db import User -from rhodecode.translation import _ log = logging.getLogger(__name__) diff --git a/rhodecode/authentication/plugins/auth_jasig_cas.py b/rhodecode/authentication/plugins/auth_jasig_cas.py --- a/rhodecode/authentication/plugins/auth_jasig_cas.py +++ b/rhodecode/authentication/plugins/auth_jasig_cas.py @@ -30,10 +30,9 @@ import rhodecode import urllib import urllib2 -from pylons.i18n.translation import lazy_ugettext as _ -from sqlalchemy.ext.hybrid import hybrid_property - -from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin +from rhodecode.translation import _ +from rhodecode.authentication.base import ( + RhodeCodeExternalAuthPlugin, hybrid_property) from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase from rhodecode.authentication.routes import AuthnPluginResourceBase from rhodecode.lib.colander_utils import strip_whitespace diff --git a/rhodecode/authentication/plugins/auth_ldap.py b/rhodecode/authentication/plugins/auth_ldap.py --- a/rhodecode/authentication/plugins/auth_ldap.py +++ b/rhodecode/authentication/plugins/auth_ldap.py @@ -27,10 +27,9 @@ import colander import logging import traceback -from pylons.i18n.translation import lazy_ugettext as _ -from sqlalchemy.ext.hybrid import hybrid_property - -from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin +from rhodecode.translation import _ +from rhodecode.authentication.base import ( + RhodeCodeExternalAuthPlugin, chop_at, hybrid_property) from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase from rhodecode.authentication.routes import AuthnPluginResourceBase from rhodecode.lib.colander_utils import strip_whitespace @@ -72,14 +71,15 @@ class LdapSettingsSchema(AuthnPluginSett host = colander.SchemaNode( colander.String(), default='', - description=_('Host of the LDAP Server'), + description=_('Host of the LDAP Server \n' + '(e.g., 192.168.2.154, or ldap-server.domain.com'), preparer=strip_whitespace, title=_('LDAP Host'), widget='string') port = colander.SchemaNode( colander.Int(), default=389, - description=_('Port that the LDAP server is listening on'), + description=_('Custom port that the LDAP server is listening on. Default: 389'), preparer=strip_whitespace, title=_('Port'), validator=colander.Range(min=0, max=65536), @@ -87,7 +87,9 @@ class LdapSettingsSchema(AuthnPluginSett dn_user = colander.SchemaNode( colander.String(), default='', - description=_('User to connect to LDAP'), + description=_('Optional user DN/account to connect to LDAP if authentication is required. \n' + 'e.g., cn=admin,dc=mydomain,dc=com, or ' + 'uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com'), missing='', preparer=strip_whitespace, title=_('Account'), @@ -95,7 +97,7 @@ class LdapSettingsSchema(AuthnPluginSett dn_pass = colander.SchemaNode( colander.String(), default='', - description=_('Password to connect to LDAP'), + description=_('Password to authenticate for given user DN.'), missing='', preparer=strip_whitespace, title=_('Password'), @@ -117,7 +119,9 @@ class LdapSettingsSchema(AuthnPluginSett base_dn = colander.SchemaNode( colander.String(), default='', - description=_('Base DN to search (e.g., dc=mydomain,dc=com)'), + description=_('Base DN to search. Dynamic bind is supported. Add `$login` marker ' + 'in it to be replaced with current user credentials \n' + '(e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)'), missing='', preparer=strip_whitespace, title=_('Base DN'), @@ -125,22 +129,25 @@ class LdapSettingsSchema(AuthnPluginSett filter = colander.SchemaNode( colander.String(), default='', - description=_('Filter to narrow results (e.g., ou=Users, etc)'), + description=_('Filter to narrow results \n' + '(e.g., (&(objectCategory=Person)(objectClass=user)), or \n' + '(memberof=cn=rc-login,ou=groups,ou=company,dc=mydomain,dc=com)))'), missing='', preparer=strip_whitespace, title=_('LDAP Search Filter'), widget='string') + search_scope = colander.SchemaNode( colander.String(), - default=search_scope_choices[0], - description=_('How deep to search LDAP'), + default=search_scope_choices[2], + description=_('How deep to search LDAP. If unsure set to SUBTREE'), title=_('LDAP Search Scope'), validator=colander.OneOf(search_scope_choices), widget='select') attr_login = colander.SchemaNode( colander.String(), - default='', - description=_('LDAP Attribute to map to user name'), + default='uid', + description=_('LDAP Attribute to map to user name (e.g., uid, or sAMAccountName)'), preparer=strip_whitespace, title=_('Login Attribute'), missing_msg=_('The LDAP Login attribute of the CN must be specified'), @@ -148,7 +155,7 @@ class LdapSettingsSchema(AuthnPluginSett attr_firstname = colander.SchemaNode( colander.String(), default='', - description=_('LDAP Attribute to map to first name'), + description=_('LDAP Attribute to map to first name (e.g., givenName)'), missing='', preparer=strip_whitespace, title=_('First Name Attribute'), @@ -156,7 +163,7 @@ class LdapSettingsSchema(AuthnPluginSett attr_lastname = colander.SchemaNode( colander.String(), default='', - description=_('LDAP Attribute to map to last name'), + description=_('LDAP Attribute to map to last name (e.g., sn)'), missing='', preparer=strip_whitespace, title=_('Last Name Attribute'), @@ -164,7 +171,9 @@ class LdapSettingsSchema(AuthnPluginSett attr_email = colander.SchemaNode( colander.String(), default='', - description=_('LDAP Attribute to map to email address'), + description=_('LDAP Attribute to map to email address (e.g., mail).\n' + 'Emails are a crucial part of RhodeCode. \n' + 'If possible add a valid email attribute to ldap users.'), missing='', preparer=strip_whitespace, title=_('Email Attribute'), @@ -182,7 +191,7 @@ class AuthLdap(object): def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, search_scope='SUBTREE', attr_login='uid', - ldap_filter='(&(objectClass=user)(!(objectClass=computer)))'): + ldap_filter=None): if ldap == Missing: raise LdapImportError("Missing or incompatible ldap library") @@ -236,14 +245,13 @@ class AuthLdap(object): server.start_tls_s() if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: - log.debug('Trying simple_bind with password and given DN: %s', + log.debug('Trying simple_bind with password and given login DN: %s', self.LDAP_BIND_DN) server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) return server def get_uid(self, username): - from rhodecode.lib.helpers import chop_at uid = username for server_addr in self.SERVER_ADDRESSES: uid = chop_at(username, "@%s" % server_addr) @@ -292,8 +300,11 @@ class AuthLdap(object): self.BASE_DN, self.SEARCH_SCOPE, filter_) if not lobjects: + log.debug("No matching LDAP objects for authentication " + "of UID:'%s' username:(%s)", uid, username) raise ldap.NO_SUCH_OBJECT() + log.debug('Found matching ldap object, trying to authenticate') for (dn, _attrs) in lobjects: if dn is None: continue @@ -304,15 +315,13 @@ class AuthLdap(object): break else: - log.debug("No matching LDAP objects for authentication " - "of '%s' (%s)", uid, username) raise LdapPasswordError('Failed to authenticate user ' 'with given password') except ldap.NO_SUCH_OBJECT: log.debug("LDAP says no such user '%s' (%s), org_exc:", uid, username, exc_info=True) - raise LdapUsernameError() + raise LdapUsernameError('Unable to find user') except ldap.SERVER_DOWN: org_exc = traceback.format_exc() raise LdapConnectionError( @@ -447,7 +456,7 @@ class RhodeCodeAuthPlugin(RhodeCodeExter 'email': get_ldap_attr('attr_email') or email, 'admin': admin, 'active': active, - "active_from_extern": None, + 'active_from_extern': None, 'extern_name': user_dn, 'extern_type': extern_type, } diff --git a/rhodecode/authentication/plugins/auth_pam.py b/rhodecode/authentication/plugins/auth_pam.py --- a/rhodecode/authentication/plugins/auth_pam.py +++ b/rhodecode/authentication/plugins/auth_pam.py @@ -17,6 +17,7 @@ # This program is dual-licensed. If you wish to learn more about the # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ + """ RhodeCode authentication library for PAM """ @@ -29,10 +30,9 @@ import pwd import re import socket -from pylons.i18n.translation import lazy_ugettext as _ -from sqlalchemy.ext.hybrid import hybrid_property - -from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin +from rhodecode.translation import _ +from rhodecode.authentication.base import ( + RhodeCodeExternalAuthPlugin, hybrid_property) from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase from rhodecode.authentication.routes import AuthnPluginResourceBase from rhodecode.lib.colander_utils import strip_whitespace diff --git a/rhodecode/authentication/plugins/auth_rhodecode.py b/rhodecode/authentication/plugins/auth_rhodecode.py --- a/rhodecode/authentication/plugins/auth_rhodecode.py +++ b/rhodecode/authentication/plugins/auth_rhodecode.py @@ -25,9 +25,8 @@ RhodeCode authentication plugin for buil import logging from pylons.i18n.translation import lazy_ugettext as _ -from sqlalchemy.ext.hybrid import hybrid_property -from rhodecode.authentication.base import RhodeCodeAuthPluginBase +from rhodecode.authentication.base import RhodeCodeAuthPluginBase, hybrid_property from rhodecode.authentication.routes import AuthnPluginResourceBase from rhodecode.lib.utils2 import safe_str from rhodecode.model.db import User diff --git a/rhodecode/authentication/plugins/auth_token.py b/rhodecode/authentication/plugins/auth_token.py --- a/rhodecode/authentication/plugins/auth_token.py +++ b/rhodecode/authentication/plugins/auth_token.py @@ -24,10 +24,9 @@ RhodeCode authentication token plugin fo import logging -from sqlalchemy.ext.hybrid import hybrid_property - from rhodecode.translation import _ -from rhodecode.authentication.base import RhodeCodeAuthPluginBase, VCS_TYPE +from rhodecode.authentication.base import ( + RhodeCodeAuthPluginBase, VCS_TYPE, hybrid_property) from rhodecode.authentication.routes import AuthnPluginResourceBase from rhodecode.model.db import User, UserApiKeys diff --git a/rhodecode/authentication/schema.py b/rhodecode/authentication/schema.py --- a/rhodecode/authentication/schema.py +++ b/rhodecode/authentication/schema.py @@ -40,10 +40,11 @@ class AuthnPluginSettingsSchemaBase(cola cache_ttl = colander.SchemaNode( colander.Int(), default=0, - description=_('Amount of seconds to cache the authentication ' - 'call for this plugin. Useful for long calls like ' - 'LDAP to improve the responsiveness of the ' - 'authentication system (0 means disabled).'), + description=_('Amount of seconds to cache the authentication response' + 'call for this plugin. \n' + 'Useful for long calls like LDAP to improve the ' + 'performance of the authentication system ' + '(0 means disabled).'), missing=0, title=_('Auth Cache TTL'), validator=colander.Range(min=0, max=None), diff --git a/rhodecode/events/base.py b/rhodecode/events/base.py --- a/rhodecode/events/base.py +++ b/rhodecode/events/base.py @@ -57,7 +57,12 @@ class RhodecodeEvent(object): def actor(self): auth_user = self.auth_user if auth_user: - return auth_user.get_instance() + instance = auth_user.get_instance() + if not instance: + return AttributeDict(dict( + username=auth_user.username + )) + return SYSTEM_USER @property diff --git a/rhodecode/public/css/main-content.less b/rhodecode/public/css/main-content.less --- a/rhodecode/public/css/main-content.less +++ b/rhodecode/public/css/main-content.less @@ -61,6 +61,7 @@ .help-block { display: block; margin-left: @label-width; + white-space: pre; } .action_button { @@ -116,7 +117,7 @@ float: none; margin-left: @label-width; - .help-block{ + .help-block { margin-left: 0; } } diff --git a/rhodecode/templates/admin/auth/plugin_settings.mako b/rhodecode/templates/admin/auth/plugin_settings.mako --- a/rhodecode/templates/admin/auth/plugin_settings.mako +++ b/rhodecode/templates/admin/auth/plugin_settings.mako @@ -111,6 +111,7 @@ $("#tls_kind").select2(select2Options); $("#tls_reqcert").select2(select2Options); $("#search_scope").select2(select2Options); + $("#group_extraction_type").select2(select2Options); });