diff --git a/docs/api/api.rst b/docs/api/api.rst --- a/docs/api/api.rst +++ b/docs/api/api.rst @@ -158,9 +158,9 @@ INPUT:: args : { "username" : "", "password" : "", - "firstname" : "", - "lastname" : "", - "email" : "" + "email" : "", + "firstname" : " = None", + "lastname" : " = None", "active" : " = True", "admin" : " = False", "ldap_dn" : " = None" diff --git a/docs/changelog.rst b/docs/changelog.rst --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -35,6 +35,7 @@ news based on user defined regular expression - added linking of changesets in commit messages - new compact changelog with expandable commit messages +- firstname and lastname are optional in user creation fixes ----- diff --git a/rhodecode/controllers/api/api.py b/rhodecode/controllers/api/api.py --- a/rhodecode/controllers/api/api.py +++ b/rhodecode/controllers/api/api.py @@ -131,17 +131,17 @@ class ApiController(JSONRPCController): return result @HasPermissionAllDecorator('hg.admin') - def create_user(self, apiuser, username, password, firstname, - lastname, email, active=True, admin=False, ldap_dn=None): + def create_user(self, apiuser, username, password, email, firstname=None, + lastname=None, active=True, admin=False, ldap_dn=None): """ Create new user or updates current one :param apiuser: :param username: :param password: + :param email: :param name: :param lastname: - :param email: :param active: :param admin: :param ldap_dn: diff --git a/rhodecode/lib/auth.py b/rhodecode/lib/auth.py --- a/rhodecode/lib/auth.py +++ b/rhodecode/lib/auth.py @@ -129,6 +129,7 @@ def get_crypt_password(password): def check_password(password, hashed): return RhodeCodeCrypto.hash_check(password, hashed) + def generate_api_key(str_, salt=None): """ Generates API KEY from given string @@ -237,6 +238,7 @@ def authenticate(username, password): pass return False + def login_container_auth(username): user = User.get_by_username(username) if user is None: @@ -260,6 +262,7 @@ def login_container_auth(username): user.username) return user + def get_container_username(environ, config): username = None @@ -278,6 +281,7 @@ def get_container_username(environ, conf return username + class AuthUser(object): """ A simple object that handles all attributes of user in RhodeCode @@ -302,6 +306,7 @@ class AuthUser(object): self.permissions = {} self._api_key = api_key self.propagate_data() + self._instance = None def propagate_data(self): user_model = UserModel() @@ -350,10 +355,6 @@ class AuthUser(object): def is_admin(self): return self.admin - @property - def full_contact(self): - return '%s %s <%s>' % (self.name, self.lastname, self.email) - def __repr__(self): return "" % (self.user_id, self.username, self.is_authenticated) @@ -363,9 +364,9 @@ class AuthUser(object): self.is_authenticated = authenticated def get_cookie_store(self): - return {'username':self.username, + return {'username': self.username, 'user_id': self.user_id, - 'is_authenticated':self.is_authenticated} + 'is_authenticated': self.is_authenticated} @classmethod def from_cookie_store(cls, cookie_store): @@ -374,6 +375,7 @@ class AuthUser(object): api_key = cookie_store.get('api_key') return AuthUser(user_id, api_key, username) + def set_available_permissions(config): """ This function will propagate pylons globals with all available defined @@ -388,7 +390,7 @@ def set_available_permissions(config): try: sa = meta.Session all_perms = sa.query(Permission).all() - except: + except Exception: pass finally: meta.Session.remove() diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -299,6 +299,11 @@ class User(Base, BaseModel): return '%s %s' % (self.name, self.lastname) @property + def full_name_or_username(self): + return ('%s %s' % (self.name, self.lastname) + if (self.name and self.lastname) else self.username) + + @property def full_contact(self): return '%s %s <%s>' % (self.name, self.lastname, self.email) @@ -354,8 +359,13 @@ class User(Base, BaseModel): log.debug('updated user %s lastlogin', self.username) def __json__(self): - return dict(email=self.email, - full_name=self.full_name) + return dict( + email=self.email, + full_name=self.full_name, + full_name_or_username=self.full_name_or_username, + short_contact=self.short_contact, + full_contact=self.full_contact + ) class UserLog(Base, BaseModel): diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -51,13 +51,17 @@ class State_obj(object): # VALIDATORS #============================================================================== class ValidAuthToken(formencode.validators.FancyValidator): - messages = {'invalid_token':_('Token mismatch')} + messages = {'invalid_token': _('Token mismatch')} def validate_python(self, value, state): if value != authentication_token(): - raise formencode.Invalid(self.message('invalid_token', state, - search_number=value), value, state) + raise formencode.Invalid( + self.message('invalid_token', + state, search_number=value), + value, + state + ) def ValidUsername(edit, old_data): @@ -77,11 +81,13 @@ def ValidUsername(edit, old_data): 'exists') , value, state) if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None: - raise formencode.Invalid(_('Username may only contain ' - 'alphanumeric characters ' - 'underscores, periods or dashes ' - 'and must begin with alphanumeric ' - 'character'), value, state) + raise formencode.Invalid( + _('Username may only contain alphanumeric characters ' + 'underscores, periods or dashes and must begin with ' + 'alphanumeric character'), + value, + state + ) return _ValidUsername @@ -103,15 +109,17 @@ def ValidUsersGroup(edit, old_data): if UsersGroup.get_by_group_name(value, cache=False, case_insensitive=True): raise formencode.Invalid(_('This users group ' - 'already exists') , value, + 'already exists'), value, state) if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None: - raise formencode.Invalid(_('RepoGroup name may only contain ' - 'alphanumeric characters ' - 'underscores, periods or dashes ' - 'and must begin with alphanumeric ' - 'character'), value, state) + raise formencode.Invalid( + _('RepoGroup name may only contain alphanumeric characters ' + 'underscores, periods or dashes and must begin with ' + 'alphanumeric character'), + value, + state + ) return _ValidUsersGroup @@ -177,32 +185,35 @@ class ValidPassword(formencode.validator def to_python(self, value, state): - if value: + if not value: + return - if value.get('password'): - try: - value['password'] = get_crypt_password(value['password']) - except UnicodeEncodeError: - e_dict = {'password':_('Invalid characters in password')} - raise formencode.Invalid('', value, state, error_dict=e_dict) + if value.get('password'): + try: + value['password'] = get_crypt_password(value['password']) + except UnicodeEncodeError: + e_dict = {'password': _('Invalid characters in password')} + raise formencode.Invalid('', value, state, error_dict=e_dict) - if value.get('password_confirmation'): - try: - value['password_confirmation'] = \ - get_crypt_password(value['password_confirmation']) - except UnicodeEncodeError: - e_dict = {'password_confirmation':_('Invalid characters in password')} - raise formencode.Invalid('', value, state, error_dict=e_dict) + if value.get('password_confirmation'): + try: + value['password_confirmation'] = \ + get_crypt_password(value['password_confirmation']) + except UnicodeEncodeError: + e_dict = { + 'password_confirmation': _('Invalid characters in password') + } + raise formencode.Invalid('', value, state, error_dict=e_dict) - if value.get('new_password'): - try: - value['new_password'] = \ - get_crypt_password(value['new_password']) - except UnicodeEncodeError: - e_dict = {'new_password':_('Invalid characters in password')} - raise formencode.Invalid('', value, state, error_dict=e_dict) + if value.get('new_password'): + try: + value['new_password'] = \ + get_crypt_password(value['new_password']) + except UnicodeEncodeError: + e_dict = {'new_password': _('Invalid characters in password')} + raise formencode.Invalid('', value, state, error_dict=e_dict) - return value + return value class ValidPasswordsMatch(formencode.validators.FancyValidator): @@ -224,9 +235,9 @@ class ValidAuth(formencode.validators.Fa } # error mapping - e_dict = {'username':messages['invalid_login'], - 'password':messages['invalid_password']} - e_dict_disable = {'username':messages['disabled_account']} + e_dict = {'username': messages['invalid_login'], + 'password': messages['invalid_password']} + e_dict_disable = {'username': messages['disabled_account']} def validate_python(self, value, state): password = value['password'] @@ -238,15 +249,19 @@ class ValidAuth(formencode.validators.Fa else: if user and user.active is False: log.warning('user %s is disabled', username) - raise formencode.Invalid(self.message('disabled_account', - state=State_obj), - value, state, - error_dict=self.e_dict_disable) + raise formencode.Invalid( + self.message('disabled_account', + state=State_obj), + value, state, + error_dict=self.e_dict_disable + ) else: log.warning('user %s not authenticated', username) - raise formencode.Invalid(self.message('invalid_password', - state=State_obj), value, state, - error_dict=self.e_dict) + raise formencode.Invalid( + self.message('invalid_password', + state=State_obj), value, state, + error_dict=self.e_dict + ) class ValidRepoUser(formencode.validators.FancyValidator): @@ -272,7 +287,6 @@ def ValidRepoName(edit, old_data): e_dict = {'repo_name': _('This repository name is disallowed')} raise formencode.Invalid('', value, state, error_dict=e_dict) - if value.get('repo_group'): gr = RepoGroup.get(value.get('repo_group')) group_path = gr.full_path @@ -285,7 +299,6 @@ def ValidRepoName(edit, old_data): group_path = '' repo_name_full = repo_name - value['repo_name_full'] = repo_name_full rename = old_data.get('repo_name') != repo_name_full create = not edit @@ -293,20 +306,22 @@ def ValidRepoName(edit, old_data): if group_path != '': if Repository.get_by_repo_name(repo_name_full): - e_dict = {'repo_name':_('This repository already ' - 'exists in a group "%s"') % - gr.group_name} + e_dict = { + 'repo_name': _('This repository already exists in ' + 'a group "%s"') % gr.group_name + } raise formencode.Invalid('', value, state, error_dict=e_dict) elif RepoGroup.get_by_group_name(repo_name_full): - e_dict = {'repo_name':_('There is a group with this' - ' name already "%s"') % - repo_name_full} + e_dict = { + 'repo_name': _('There is a group with this name ' + 'already "%s"') % repo_name_full + } raise formencode.Invalid('', value, state, error_dict=e_dict) elif Repository.get_by_repo_name(repo_name_full): - e_dict = {'repo_name':_('This repository ' + e_dict = {'repo_name': _('This repository ' 'already exists')} raise formencode.Invalid('', value, state, error_dict=e_dict) @@ -341,14 +356,14 @@ def ValidCloneUri(): elif value.startswith('https'): try: httpsrepository(make_ui('db'), value).capabilities - except Exception, e: + except Exception: log.error(traceback.format_exc()) raise formencode.Invalid(_('invalid clone url'), value, state) elif value.startswith('http'): try: httprepository(make_ui('db'), value).capabilities - except Exception, e: + except Exception: log.error(traceback.format_exc()) raise formencode.Invalid(_('invalid clone url'), value, state) @@ -374,7 +389,7 @@ def ValidForkType(old_data): class ValidPerms(formencode.validators.FancyValidator): - messages = {'perm_new_member_name':_('This username or users group name' + messages = {'perm_new_member_name': _('This username or users group name' ' is not valid')} def to_python(self, value, state): @@ -393,8 +408,9 @@ class ValidPerms(formencode.validators.F perms_new.append((new_member, new_perm, new_type)) elif k.startswith('u_perm_') or k.startswith('g_perm_'): member = k[7:] - t = {'u':'user', - 'g':'users_group'}[k[0]] + t = {'u': 'user', + 'g': 'users_group' + }[k[0]] if member == 'default': if value['private']: #set none for default when updating to private repo @@ -419,8 +435,9 @@ class ValidPerms(formencode.validators.F except Exception: msg = self.message('perm_new_member_name', state=State_obj) - raise formencode.Invalid(msg, value, state, - error_dict={'perm_new_member_name':msg}) + raise formencode.Invalid( + msg, value, state, error_dict={'perm_new_member_name': msg} + ) return value @@ -428,9 +445,8 @@ class ValidSettings(formencode.validator def to_python(self, value, state): # settings form can't edit user - if value.has_key('user'): + if 'user' in value: del['value']['user'] - return value @@ -440,7 +456,7 @@ class ValidPath(formencode.validators.Fa if not os.path.isdir(value): msg = _('This is not a valid path') raise formencode.Invalid(msg, value, state, - error_dict={'paths_root_path':msg}) + error_dict={'paths_root_path': msg}) return value @@ -448,12 +464,12 @@ def UniqSystemEmail(old_data): class _UniqSystemEmail(formencode.validators.FancyValidator): def to_python(self, value, state): value = value.lower() - if old_data.get('email','').lower() != value: + if old_data.get('email', '').lower() != value: user = User.get_by_email(value, case_insensitive=True) if user: raise formencode.Invalid( - _("This e-mail address is already taken"), - value, state) + _("This e-mail address is already taken"), value, state + ) return value return _UniqSystemEmail @@ -464,8 +480,9 @@ class ValidSystemEmail(formencode.valida value = value.lower() user = User.get_by_email(value, case_insensitive=True) if user is None: - raise formencode.Invalid(_("This e-mail address doesn't exist.") , - value, state) + raise formencode.Invalid( + _("This e-mail address doesn't exist."), value, state + ) return value @@ -486,14 +503,15 @@ class AttrLoginValidator(formencode.vali def to_python(self, value, state): if not value or not isinstance(value, (str, unicode)): - raise formencode.Invalid(_("The LDAP Login attribute of the CN " - "must be specified - this is the name " - "of the attribute that is equivalent " - "to 'username'"), - value, state) + raise formencode.Invalid( + _("The LDAP Login attribute of the CN must be specified - " + "this is the name of the attribute that is equivalent " + "to 'username'"), value, state + ) return value + #============================================================================== # FORMS #============================================================================== @@ -501,22 +519,22 @@ class LoginForm(formencode.Schema): allow_extra_fields = True filter_extra_fields = True username = UnicodeString( - strip=True, - min=1, - not_empty=True, - messages={ - 'empty':_('Please enter a login'), - 'tooShort':_('Enter a value %(min)i characters long or more')} - ) + strip=True, + min=1, + not_empty=True, + messages={ + 'empty': _('Please enter a login'), + 'tooShort': _('Enter a value %(min)i characters long or more')} + ) password = UnicodeString( - strip=True, - min=3, - not_empty=True, - messages={ - 'empty':_('Please enter a password'), - 'tooShort':_('Enter %(min)i characters or more')} - ) + strip=True, + min=3, + not_empty=True, + messages={ + 'empty': _('Please enter a password'), + 'tooShort': _('Enter %(min)i characters or more')} + ) remember = StringBoolean(if_missing=False) @@ -531,15 +549,17 @@ def UserForm(edit=False, old_data={}): ValidUsername(edit, old_data)) if edit: new_password = All(UnicodeString(strip=True, min=6, not_empty=False)) - password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False)) + password_confirmation = All(UnicodeString(strip=True, min=6, + not_empty=False)) admin = StringBoolean(if_missing=False) else: password = All(UnicodeString(strip=True, min=6, not_empty=True)) - password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False)) + password_confirmation = All(UnicodeString(strip=True, min=6, + not_empty=False)) active = StringBoolean(if_missing=False) - name = UnicodeString(strip=True, min=1, not_empty=True) - lastname = UnicodeString(strip=True, min=1, not_empty=True) + name = UnicodeString(strip=True, min=1, not_empty=False) + lastname = UnicodeString(strip=True, min=1, not_empty=False) email = All(Email(not_empty=True), UniqSystemEmail(old_data)) chained_validators = [ValidPasswordsMatch, ValidPassword] @@ -592,14 +612,15 @@ def RegisterForm(edit=False, old_data={} password = All(UnicodeString(strip=True, min=6, not_empty=True)) password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True)) active = StringBoolean(if_missing=False) - name = UnicodeString(strip=True, min=1, not_empty=True) - lastname = UnicodeString(strip=True, min=1, not_empty=True) + name = UnicodeString(strip=True, min=1, not_empty=False) + lastname = UnicodeString(strip=True, min=1, not_empty=False) email = All(Email(not_empty=True), UniqSystemEmail(old_data)) chained_validators = [ValidPasswordsMatch, ValidPassword] return _RegisterForm + def PasswordResetForm(): class _PasswordResetForm(formencode.Schema): allow_extra_fields = True @@ -607,6 +628,7 @@ def PasswordResetForm(): email = All(ValidSystemEmail(), Email(not_empty=True)) return _PasswordResetForm + def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), repo_groups=[]): class _RepoForm(formencode.Schema): @@ -630,6 +652,7 @@ def RepoForm(edit=False, old_data={}, su chained_validators = [ValidRepoName(edit, old_data), ValidPerms] return _RepoForm + def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), repo_groups=[]): class _RepoForkForm(formencode.Schema): @@ -648,6 +671,7 @@ def RepoForkForm(edit=False, old_data={} return _RepoForkForm + def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), repo_groups=[]): class _RepoForm(formencode.Schema): @@ -674,6 +698,7 @@ def ApplicationSettingsForm(): return _ApplicationSettingsForm + def ApplicationUiSettingsForm(): class _ApplicationUiSettingsForm(formencode.Schema): allow_extra_fields = True @@ -687,6 +712,7 @@ def ApplicationUiSettingsForm(): return _ApplicationUiSettingsForm + def DefaultPermissionsForm(perms_choices, register_choices, create_choices): class _DefaultPermissionsForm(formencode.Schema): allow_extra_fields = True diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py --- a/rhodecode/model/user.py +++ b/rhodecode/model/user.py @@ -92,7 +92,6 @@ class UserModel(BaseModel): log.error(traceback.format_exc()) raise - def create_or_update(self, username, password, email, name, lastname, active=True, admin=False, ldap_dn=None): """ @@ -136,7 +135,6 @@ class UserModel(BaseModel): log.error(traceback.format_exc()) raise - def create_for_container_auth(self, username, attrs): """ Creates the given user if it's not already in the database @@ -231,7 +229,7 @@ class UserModel(BaseModel): body = body % (new_user.username, new_user.full_name, new_user.email) edit_url = url('edit_user', id=new_user.user_id, qualified=True) - kw = {'registered_user_url':edit_url} + kw = {'registered_user_url': edit_url} NotificationModel().create(created_by=new_user, subject=subject, body=body, recipients=None, type_=Notification.TYPE_REGISTRATION, @@ -493,7 +491,6 @@ class UserModel(BaseModel): new.permission = perm self.sa.add(new) - def revoke_perm(self, user, perm): if not isinstance(perm, Permission): raise Exception('perm needs to be an instance of Permission class ' diff --git a/rhodecode/templates/admin/users/user_add.html b/rhodecode/templates/admin/users/user_add.html --- a/rhodecode/templates/admin/users/user_add.html +++ b/rhodecode/templates/admin/users/user_add.html @@ -86,7 +86,7 @@
- ${h.checkbox('active',value=True)} + ${h.checkbox('active',value=True,checked='checked')}
diff --git a/rhodecode/templates/base/base.html b/rhodecode/templates/base/base.html --- a/rhodecode/templates/base/base.html +++ b/rhodecode/templates/base/base.html @@ -112,7 +112,7 @@ ${h.end_form()} %else: