diff rhodecode/model/user.py @ 4116:ffd45b185016 rhodecode-2.2.5-gpl

Imported some of the GPLv3'd changes from RhodeCode v2.2.5. This imports changes between changesets 21af6c4eab3d and 6177597791c2 in RhodeCode's original repository, including only changes to Python files and HTML. RhodeCode clearly licensed its changes to these files under GPLv3 in their /LICENSE file, which states the following: The Python code and integrated HTML are licensed under the GPLv3 license. (See: https://code.rhodecode.com/rhodecode/files/v2.2.5/LICENSE or http://web.archive.org/web/20140512193334/https://code.rhodecode.com/rhodecode/files/f3b123159901f15426d18e3dc395e8369f70ebe0/LICENSE for an online copy of that LICENSE file) Conservancy reviewed these changes and confirmed that they can be licensed as a whole to the Kallithea project under GPLv3-only. While some of the contents committed herein are clearly licensed GPLv3-or-later, on the whole we must assume the are GPLv3-only, since the statement above from RhodeCode indicates that they intend GPLv3-only as their license, per GPLv3ยง14 and other relevant sections of GPLv3.
author Bradley M. Kuhn <bkuhn@sfconservancy.org>
date Wed, 02 Jul 2014 19:03:13 -0400
parents 92da990f9eaf
children 7e5f8c12a3fc
line wrap: on
line diff
--- a/rhodecode/model/user.py	Wed Jul 02 19:03:10 2014 -0400
+++ b/rhodecode/model/user.py	Wed Jul 02 19:03:13 2014 -0400
@@ -1,15 +1,4 @@
 # -*- coding: utf-8 -*-
-"""
-    rhodecode.model.user
-    ~~~~~~~~~~~~~~~~~~~~
-
-    users model for RhodeCode
-
-    :created_on: Apr 9, 2010
-    :author: marcink
-    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
-    :license: GPLv3, see COPYING for more details.
-"""
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
@@ -22,24 +11,32 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+rhodecode.model.user
+~~~~~~~~~~~~~~~~~~~~
+
+users model for RhodeCode
+
+:created_on: Apr 9, 2010
+:author: marcink
+:copyright: (c) 2013 RhodeCode GmbH.
+:license: GPLv3, see LICENSE for more details.
+"""
+
 
 import logging
 import traceback
-import itertools
-import collections
 from pylons import url
 from pylons.i18n.translation import _
 
 from sqlalchemy.exc import DatabaseError
-from sqlalchemy.orm import joinedload
+
 
 from rhodecode.lib.utils2 import safe_unicode, generate_api_key, get_current_rhodecode_user
 from rhodecode.lib.caching_query import FromCache
 from rhodecode.model import BaseModel
-from rhodecode.model.db import User, Repository, Permission, \
-    UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
-    Notification, RepoGroup, UserGroupRepoGroupToPerm, \
-    UserEmailMap, UserIpMap, UserGroupUserGroupToPerm, UserGroup
+from rhodecode.model.db import User, UserToPerm, Notification, \
+    UserEmailMap, UserIpMap
 from rhodecode.lib.exceptions import DefaultUserException, \
     UserOwnsReposException
 from rhodecode.model.meta import Session
@@ -47,8 +44,6 @@
 
 log = logging.getLogger(__name__)
 
-PERM_WEIGHTS = Permission.PERM_WEIGHTS
-
 
 class UserModel(BaseModel):
     cls = User
@@ -68,7 +63,8 @@
         if case_insensitive:
             user = self.sa.query(User).filter(User.username.ilike(username))
         else:
-            user = self.sa.query(User).filter(User.username == username)
+            user = self.sa.query(User)\
+                .filter(User.username == username)
         if cache:
             user = user.options(FromCache("sql_cache_short",
                                           "get_user_%s" % username))
@@ -93,7 +89,6 @@
         }
         # raises UserCreationError if it's not allowed
         check_allowed_create_user(user_data, cur_user)
-
         from rhodecode.lib.auth import get_crypt_password
         try:
             new_user = User()
@@ -114,8 +109,8 @@
             raise
 
     def create_or_update(self, username, password, email, firstname='',
-                         lastname='', active=True, admin=False, ldap_dn=None,
-                         cur_user=None):
+                         lastname='', active=True, admin=False,
+                         extern_type=None, extern_name=None, cur_user=None):
         """
         Creates a new instance if not found, or updates current one
 
@@ -127,13 +122,14 @@
         :param lastname:
         :param active:
         :param admin:
-        :param ldap_dn:
+        :param extern_name:
+        :param extern_type:
         :param cur_user:
         """
         if not cur_user:
             cur_user = getattr(get_current_rhodecode_user(), 'username', None)
 
-        from rhodecode.lib.auth import get_crypt_password
+        from rhodecode.lib.auth import get_crypt_password, check_password
         from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
         user_data = {
             'username': username, 'password': password,
@@ -157,15 +153,24 @@
         try:
             new_user.username = username
             new_user.admin = admin
-            # set password only if creating an user or password is changed
-            if not edit or user.password != password:
-                new_user.password = get_crypt_password(password) if password else None
-                new_user.api_key = generate_api_key(username)
             new_user.email = email
             new_user.active = active
-            new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
+            new_user.extern_name = safe_unicode(extern_name) if extern_name else None
+            new_user.extern_type = safe_unicode(extern_type) if extern_type else None
             new_user.name = firstname
             new_user.lastname = lastname
+
+            if not edit:
+                new_user.api_key = generate_api_key(username)
+
+            # set password only if creating an user or password is changed
+            password_change = new_user.password and not check_password(password,
+                                                            new_user.password)
+            if not edit or password_change:
+                reason = 'new password' if edit else 'new user'
+                log.debug('Updating password reason=>%s' % (reason,))
+                new_user.password = get_crypt_password(password) if password else None
+
             self.sa.add(new_user)
 
             if not edit:
@@ -175,116 +180,13 @@
             log.error(traceback.format_exc())
             raise
 
-    def create_for_container_auth(self, username, attrs, cur_user=None):
-        """
-        Creates the given user if it's not already in the database
-
-        :param username:
-        :param attrs:
-        :param cur_user:
-        """
-        if not cur_user:
-            cur_user = getattr(get_current_rhodecode_user(), 'username', None)
-        if self.get_by_username(username, case_insensitive=True) is None:
-            # autogenerate email for container account without one
-            generate_email = lambda usr: '%s@container_auth.account' % usr
-            firstname = attrs['name']
-            lastname = attrs['lastname']
-            active = attrs.get('active', True)
-            email = attrs['email'] or generate_email(username)
-
-            from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
-            user_data = {
-                'username': username, 'password': None,
-                'email': email, 'firstname': firstname, 'lastname': lastname,
-                'active': attrs.get('active', True), 'admin': False
-            }
-            # raises UserCreationError if it's not allowed
-            check_allowed_create_user(user_data, cur_user)
-
-            try:
-                new_user = User()
-                new_user.username = username
-                new_user.password = None
-                new_user.api_key = generate_api_key(username)
-                new_user.email = email
-                new_user.active = active
-                new_user.name = firstname
-                new_user.lastname = lastname
-
-                self.sa.add(new_user)
-                log_create_user(new_user.get_dict(), cur_user)
-                return new_user
-            except (DatabaseError,):
-                log.error(traceback.format_exc())
-                self.sa.rollback()
-                raise
-        log.debug('User %s already exists. Skipping creation of account'
-                  ' for container auth.', username)
-        return None
-
-    def create_ldap(self, username, password, user_dn, attrs, cur_user=None):
-        """
-        Checks if user is in database, if not creates this user marked
-        as ldap user
-
-        :param username:
-        :param password:
-        :param user_dn:
-        :param attrs:
-        :param cur_user:
-        """
-        if not cur_user:
-            cur_user = getattr(get_current_rhodecode_user(), 'username', None)
-        from rhodecode.lib.auth import get_crypt_password
-        log.debug('Checking for such ldap account in RhodeCode database')
-        if self.get_by_username(username, case_insensitive=True) is None:
-            # autogenerate email for container account without one
-            generate_email = lambda usr: '%s@ldap.account' % usr
-            password = get_crypt_password(password)
-            firstname = attrs['name']
-            lastname = attrs['lastname']
-            active = attrs.get('active', True)
-            email = attrs['email'] or generate_email(username)
-
-            from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
-            user_data = {
-                'username': username, 'password': password,
-                'email': email, 'firstname': firstname, 'lastname': lastname,
-                'active': attrs.get('active', True), 'admin': False
-            }
-            # raises UserCreationError if it's not allowed
-            check_allowed_create_user(user_data, cur_user)
-
-            try:
-                new_user = User()
-                username = username.lower()
-                # add ldap account always lowercase
-                new_user.username = username
-                new_user.password = password
-                new_user.api_key = generate_api_key(username)
-                new_user.email = email
-                new_user.active = active
-                new_user.ldap_dn = safe_unicode(user_dn)
-                new_user.name = firstname
-                new_user.lastname = lastname
-                self.sa.add(new_user)
-
-                log_create_user(new_user.get_dict(), cur_user)
-                return new_user
-            except (DatabaseError,):
-                log.error(traceback.format_exc())
-                self.sa.rollback()
-                raise
-        log.debug('this %s user exists skipping creation of ldap account',
-                  username)
-        return None
-
     def create_registration(self, form_data):
         from rhodecode.model.notification import NotificationModel
 
         try:
             form_data['admin'] = False
+            form_data['extern_name'] = 'rhodecode'
+            form_data['extern_type'] = 'rhodecode'
             new_user = self.create(form_data)
 
             self.sa.add(new_user)
@@ -313,18 +215,19 @@
         from rhodecode.lib.auth import get_crypt_password
         try:
             user = self.get(user_id, cache=False)
-            if user.username == 'default':
+            if user.username == User.DEFAULT_USER:
                 raise DefaultUserException(
-                                _("You can't Edit this user since it's"
-                                  " crucial for entire application"))
+                                _("You can't Edit this user since it's "
+                                  "crucial for entire application"))
 
             for k, v in form_data.items():
                 if k in skip_attrs:
                     continue
                 if k == 'new_password' and v:
                     user.password = get_crypt_password(v)
-                    user.api_key = generate_api_key(user.username)
                 else:
+                    # old legacy thing orm models store firstname as name,
+                    # need proper refactor to username
                     if k == 'firstname':
                         k = 'name'
                     setattr(user, k, v)
@@ -337,7 +240,7 @@
         from rhodecode.lib.auth import get_crypt_password
         try:
             user = self._get_user(user)
-            if user.username == 'default':
+            if user.username == User.DEFAULT_USER:
                 raise DefaultUserException(
                     _("You can't Edit this user since it's"
                       " crucial for entire application")
@@ -346,7 +249,6 @@
             for k, v in kwargs.items():
                 if k == 'password' and v:
                     v = get_crypt_password(v)
-                    user.api_key = generate_api_key(user.username)
 
                 setattr(user, k, v)
             self.sa.add(user)
@@ -361,7 +263,7 @@
         user = self._get_user(user)
 
         try:
-            if user.username == 'default':
+            if user.username == User.DEFAULT_USER:
                 raise DefaultUserException(
                     _(u"You can't remove this user since it's"
                       " crucial for entire application")
@@ -418,7 +320,6 @@
                             auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
             if user:
                 user.password = auth.get_crypt_password(new_passwd)
-                user.api_key = auth.generate_api_key(user.username)
                 Session().add(user)
                 Session().commit()
                 log.info('change password for %s' % user_email)
@@ -441,7 +342,7 @@
 
         return True
 
-    def fill_data(self, auth_user, user_id=None, api_key=None):
+    def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
         """
         Fetches auth_user by user_id,or api_key if present.
         Fills auth_user attributes with those taken from database.
@@ -451,20 +352,25 @@
         :param auth_user: instance of user to set attributes
         :param user_id: user id to fetch by
         :param api_key: api key to fetch by
+        :param username: username to fetch by
         """
-        if user_id is None and api_key is None:
-            raise Exception('You need to pass user_id or api_key')
+        if user_id is None and api_key is None and username is None:
+            raise Exception('You need to pass user_id, api_key or username')
 
         try:
-            if api_key:
+            dbuser = None
+            if user_id:
+                dbuser = self.get(user_id)
+            elif api_key:
                 dbuser = self.get_by_api_key(api_key)
-            else:
-                dbuser = self.get(user_id)
+            elif username:
+                dbuser = self.get_by_username(username)
 
             if dbuser is not None and dbuser.active:
                 log.debug('filling %s data' % dbuser)
-                for k, v in dbuser.get_dict().items():
-                    setattr(auth_user, k, v)
+                for k, v in dbuser.get_dict().iteritems():
+                    if k not in ['api_keys', 'permissions']:
+                        setattr(auth_user, k, v)
             else:
                 return False
 
@@ -475,296 +381,6 @@
 
         return True
 
-    def fill_perms(self, user, explicit=True, algo='higherwin'):
-        """
-        Fills user permission attribute with permissions taken from database
-        works for permissions given for repositories, and for permissions that
-        are granted to groups
-
-        :param user: user instance to fill his perms
-        :param explicit: In case there are permissions both for user and a group
-            that user is part of, explicit flag will defiine if user will
-            explicitly override permissions from group, if it's False it will
-            make decision based on the algo
-        :param algo: algorithm to decide what permission should be choose if
-            it's multiple defined, eg user in two different groups. It also
-            decides if explicit flag is turned off how to specify the permission
-            for case when user is in a group + have defined separate permission
-        """
-        RK = 'repositories'
-        GK = 'repositories_groups'
-        UK = 'user_groups'
-        GLOBAL = 'global'
-        user.permissions[RK] = {}
-        user.permissions[GK] = {}
-        user.permissions[UK] = {}
-        user.permissions[GLOBAL] = set()
-
-        def _choose_perm(new_perm, cur_perm):
-            new_perm_val = PERM_WEIGHTS[new_perm]
-            cur_perm_val = PERM_WEIGHTS[cur_perm]
-            if algo == 'higherwin':
-                if new_perm_val > cur_perm_val:
-                    return new_perm
-                return cur_perm
-            elif algo == 'lowerwin':
-                if new_perm_val < cur_perm_val:
-                    return new_perm
-                return cur_perm
-
-        #======================================================================
-        # fetch default permissions
-        #======================================================================
-        default_user = User.get_by_username('default', cache=True)
-        default_user_id = default_user.user_id
-
-        default_repo_perms = Permission.get_default_perms(default_user_id)
-        default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
-        default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
-
-        if user.is_admin:
-            #==================================================================
-            # admin user have all default rights for repositories
-            # and groups set to admin
-            #==================================================================
-            user.permissions[GLOBAL].add('hg.admin')
-
-            # repositories
-            for perm in default_repo_perms:
-                r_k = perm.UserRepoToPerm.repository.repo_name
-                p = 'repository.admin'
-                user.permissions[RK][r_k] = p
-
-            # repository groups
-            for perm in default_repo_groups_perms:
-                rg_k = perm.UserRepoGroupToPerm.group.group_name
-                p = 'group.admin'
-                user.permissions[GK][rg_k] = p
-
-            # user groups
-            for perm in default_user_group_perms:
-                u_k = perm.UserUserGroupToPerm.user_group.users_group_name
-                p = 'usergroup.admin'
-                user.permissions[UK][u_k] = p
-            return user
-
-        #==================================================================
-        # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
-        #==================================================================
-        uid = user.user_id
-
-        # default global permissions taken fron the default user
-        default_global_perms = self.sa.query(UserToPerm)\
-            .filter(UserToPerm.user_id == default_user_id)
-
-        for perm in default_global_perms:
-            user.permissions[GLOBAL].add(perm.permission.permission_name)
-
-        # defaults for repositories, taken from default user
-        for perm in default_repo_perms:
-            r_k = perm.UserRepoToPerm.repository.repo_name
-            if perm.Repository.private and not (perm.Repository.user_id == uid):
-                # disable defaults for private repos,
-                p = 'repository.none'
-            elif perm.Repository.user_id == uid:
-                # set admin if owner
-                p = 'repository.admin'
-            else:
-                p = perm.Permission.permission_name
-
-            user.permissions[RK][r_k] = p
-
-        # defaults for repository groups taken from default user permission
-        # on given group
-        for perm in default_repo_groups_perms:
-            rg_k = perm.UserRepoGroupToPerm.group.group_name
-            p = perm.Permission.permission_name
-            user.permissions[GK][rg_k] = p
-
-        # defaults for user groups taken from default user permission
-        # on given user group
-        for perm in default_user_group_perms:
-            u_k = perm.UserUserGroupToPerm.user_group.users_group_name
-            p = perm.Permission.permission_name
-            user.permissions[UK][u_k] = p
-
-        #======================================================================
-        # !! OVERRIDE GLOBALS !! with user permissions if any found
-        #======================================================================
-        # those can be configured from groups or users explicitly
-        _configurable = set([
-            'hg.fork.none', 'hg.fork.repository',
-            'hg.create.none', 'hg.create.repository',
-            'hg.usergroup.create.false', 'hg.usergroup.create.true'
-        ])
-
-        # USER GROUPS comes first
-        # user group global permissions
-        user_perms_from_users_groups = self.sa.query(UserGroupToPerm)\
-            .options(joinedload(UserGroupToPerm.permission))\
-            .join((UserGroupMember, UserGroupToPerm.users_group_id ==
-                   UserGroupMember.users_group_id))\
-            .filter(UserGroupMember.user_id == uid)\
-            .order_by(UserGroupToPerm.users_group_id)\
-            .all()
-        #need to group here by groups since user can be in more than one group
-        _grouped = [[x, list(y)] for x, y in
-                    itertools.groupby(user_perms_from_users_groups,
-                                      lambda x:x.users_group)]
-        for gr, perms in _grouped:
-            # since user can be in multiple groups iterate over them and
-            # select the lowest permissions first (more explicit)
-            ##TODO: do this^^
-            if not gr.inherit_default_permissions:
-                # NEED TO IGNORE all configurable permissions and
-                # replace them with explicitly set
-                user.permissions[GLOBAL] = user.permissions[GLOBAL]\
-                                                .difference(_configurable)
-            for perm in perms:
-                user.permissions[GLOBAL].add(perm.permission.permission_name)
-
-        # user specific global permissions
-        user_perms = self.sa.query(UserToPerm)\
-                .options(joinedload(UserToPerm.permission))\
-                .filter(UserToPerm.user_id == uid).all()
-
-        if not user.inherit_default_permissions:
-            # NEED TO IGNORE all configurable permissions and
-            # replace them with explicitly set
-            user.permissions[GLOBAL] = user.permissions[GLOBAL]\
-                                            .difference(_configurable)
-
-            for perm in user_perms:
-                user.permissions[GLOBAL].add(perm.permission.permission_name)
-        ## END GLOBAL PERMISSIONS
-
-        #======================================================================
-        # !! PERMISSIONS FOR REPOSITORIES !!
-        #======================================================================
-        #======================================================================
-        # check if user is part of user groups for this repository and
-        # fill in his permission from it. _choose_perm decides of which
-        # permission should be selected based on selected method
-        #======================================================================
-
-        # user group for repositories permissions
-        user_repo_perms_from_users_groups = \
-         self.sa.query(UserGroupRepoToPerm, Permission, Repository,)\
-            .join((Repository, UserGroupRepoToPerm.repository_id ==
-                   Repository.repo_id))\
-            .join((Permission, UserGroupRepoToPerm.permission_id ==
-                   Permission.permission_id))\
-            .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
-                   UserGroupMember.users_group_id))\
-            .filter(UserGroupMember.user_id == uid)\
-            .all()
-
-        multiple_counter = collections.defaultdict(int)
-        for perm in user_repo_perms_from_users_groups:
-            r_k = perm.UserGroupRepoToPerm.repository.repo_name
-            multiple_counter[r_k] += 1
-            p = perm.Permission.permission_name
-            cur_perm = user.permissions[RK][r_k]
-
-            if perm.Repository.user_id == uid:
-                # set admin if owner
-                p = 'repository.admin'
-            else:
-                if multiple_counter[r_k] > 1:
-                    p = _choose_perm(p, cur_perm)
-            user.permissions[RK][r_k] = p
-
-        # user explicit permissions for repositories, overrides any specified
-        # by the group permission
-        user_repo_perms = Permission.get_default_perms(uid)
-        for perm in user_repo_perms:
-            r_k = perm.UserRepoToPerm.repository.repo_name
-            cur_perm = user.permissions[RK][r_k]
-            # set admin if owner
-            if perm.Repository.user_id == uid:
-                p = 'repository.admin'
-            else:
-                p = perm.Permission.permission_name
-                if not explicit:
-                    p = _choose_perm(p, cur_perm)
-            user.permissions[RK][r_k] = p
-
-        #======================================================================
-        # !! PERMISSIONS FOR REPOSITORY GROUPS !!
-        #======================================================================
-        #======================================================================
-        # check if user is part of user groups for this repository groups and
-        # fill in his permission from it. _choose_perm decides of which
-        # permission should be selected based on selected method
-        #======================================================================
-        # user group for repo groups permissions
-        user_repo_group_perms_from_users_groups = \
-         self.sa.query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
-         .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((Permission, UserGroupRepoGroupToPerm.permission_id
-                == Permission.permission_id))\
-         .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
-                == UserGroupMember.users_group_id))\
-         .filter(UserGroupMember.user_id == uid)\
-         .all()
-
-        multiple_counter = collections.defaultdict(int)
-        for perm in user_repo_group_perms_from_users_groups:
-            g_k = perm.UserGroupRepoGroupToPerm.group.group_name
-            multiple_counter[g_k] += 1
-            p = perm.Permission.permission_name
-            cur_perm = user.permissions[GK][g_k]
-            if multiple_counter[g_k] > 1:
-                p = _choose_perm(p, cur_perm)
-            user.permissions[GK][g_k] = p
-
-        # user explicit permissions for repository groups
-        user_repo_groups_perms = Permission.get_default_group_perms(uid)
-        for perm in user_repo_groups_perms:
-            rg_k = perm.UserRepoGroupToPerm.group.group_name
-            p = perm.Permission.permission_name
-            cur_perm = user.permissions[GK][rg_k]
-            if not explicit:
-                p = _choose_perm(p, cur_perm)
-            user.permissions[GK][rg_k] = p
-
-        #======================================================================
-        # !! PERMISSIONS FOR USER GROUPS !!
-        #======================================================================
-        # user group for user group permissions
-        user_group_user_groups_perms = \
-         self.sa.query(UserGroupUserGroupToPerm, Permission, UserGroup)\
-         .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
-                == UserGroup.users_group_id))\
-         .join((Permission, UserGroupUserGroupToPerm.permission_id
-                == Permission.permission_id))\
-         .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
-                == UserGroupMember.users_group_id))\
-         .filter(UserGroupMember.user_id == uid)\
-         .all()
-
-        multiple_counter = collections.defaultdict(int)
-        for perm in user_group_user_groups_perms:
-            g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
-            multiple_counter[g_k] += 1
-            p = perm.Permission.permission_name
-            cur_perm = user.permissions[UK][g_k]
-            if multiple_counter[g_k] > 1:
-                p = _choose_perm(p, cur_perm)
-            user.permissions[UK][g_k] = p
-
-        #user explicit permission for user groups
-        user_user_groups_perms = Permission.get_default_user_group_perms(uid)
-        for perm in user_user_groups_perms:
-            u_k = perm.UserUserGroupToPerm.user_group.users_group_name
-            p = perm.Permission.permission_name
-            cur_perm = user.permissions[UK][u_k]
-            if not explicit:
-                p = _choose_perm(p, cur_perm)
-            user.permissions[UK][u_k] = p
-
-        return user
-
     def has_perm(self, user, perm):
         perm = self._get_perm(perm)
         user = self._get_user(user)
@@ -792,6 +408,7 @@
         new.user = user
         new.permission = perm
         self.sa.add(new)
+        return new
 
     def revoke_perm(self, user, perm):
         """