From 7b8643889f05044743a7abec398a96c3eb1d2397 Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Aug 28 2023 07:18:14 +0000
Subject: [PATCH 1/5] Inherit group permissions


Related: https://pagure.io/koji/issue/3849

---

diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py
index 69b262d..adb927f 100644
--- a/cli/koji_cli/commands.py
+++ b/cli/koji_cli/commands.py
@@ -2168,15 +2168,19 @@ def handle_list_permissions(goptions, session, args):
         parser.error("This command takes no arguments")
     activate_session(session, goptions)
     perms = []
+    if options.mine:
+        options.user = session.getLoggedInUser()['id']
     if options.user:
         user = session.getUser(options.user)
         if not user:
             error("No such user: %s" % options.user)
-        for p in session.getUserPerms(user['id']):
-            perms.append({'name': p})
-    elif options.mine:
-        for p in session.getPerms():
-            perms.append({'name': p})
+        all = set(session.getUserPerms(user['id']))
+        own = set(session.getUserPerms(user['id'], with_groups=False))
+        for p in all:
+            p = {'name': p}
+            if p['name'] not in own:
+                p['description'] = '[inherited]'
+            perms.append(p)
     else:
         for p in session.getAllPerms():
             perms.append({'name': p['name'], 'description': p['description']})
diff --git a/docs/source/permissions.rst b/docs/source/permissions.rst
index 700f1be..7576f80 100644
--- a/docs/source/permissions.rst
+++ b/docs/source/permissions.rst
@@ -17,6 +17,8 @@ referenced in :doc:`hub policies <defining_hub_policies>`. Note, that you need
 to first understand the policy mechanism as most permissions are reflected in
 policy rules.
 
+Any user can be part of user group. User group permissions are inherited by the user.
+
 
 Permission management
 =====================
diff --git a/kojihub/auth.py b/kojihub/auth.py
index dc52b81..5ea2ded 100644
--- a/kojihub/auth.py
+++ b/kojihub/auth.py
@@ -772,7 +772,7 @@ def get_user_groups(user_id):
     are the group names"""
     t_group = koji.USERTYPES['GROUP']
     query = QueryProcessor(tables=['user_groups'], columns=['group_id', 'name'],
-                           clauses=['active = TRUE', 'users.usertype=%(t_group)i',
+                           clauses=['active IS TRUE', 'users.usertype=%(t_group)i',
                                     'user_id=%(user_id)i'],
                            joins=['users ON group_id = users.id'],
                            values={'t_group': t_group, 'user_id': user_id})
@@ -782,13 +782,30 @@ def get_user_groups(user_id):
     return groups
 
 
-def get_user_perms(user_id):
+def get_user_perms(user_id, with_groups=True):
+    # individual permissions
     query = QueryProcessor(tables=['user_perms'], columns=['name'],
-                           clauses=['active = TRUE', 'user_id=%(user_id)s'],
+                           clauses=['active IS TRUE', 'user_id=%(user_id)s'],
                            joins=['permissions ON perm_id = permissions.id'],
                            values={'user_id': user_id})
     result = query.execute()
-    return [r['name'] for r in result]
+    perms = {r['name'] for r in result}
+
+    # inherited group permissions
+    if with_groups:
+        query = QueryProcessor(tables=['user_groups'], columns=['name'],
+                               clauses=[
+                                   'user_groups.active IS TRUE',
+                                   'user_perms.active IS TRUE',
+                                   'user_groups.user_id=%(user_id)s'],
+                               joins=[
+                                   'LEFT JOIN user_perms ON '
+                                   'user_perms.user_id = user_groups.group_id',
+                                   'permissions ON perm_id = permissions.id'],
+                               values={'user_id': user_id})
+        result = query.execute()
+        perms |= {r['name'] for r in result}
+    return list(perms)
 
 
 def get_user_data(user_id):
diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index 775cb5d..3e07748 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -13382,13 +13382,13 @@ class RootExports(object):
         """Get a list of the permissions granted to the currently logged-in user."""
         return context.session.getPerms()
 
-    def getUserPerms(self, userID=None):
+    def getUserPerms(self, userID=None, with_groups=True):
         """Get a list of the permissions granted to the user with the given ID/name.
         Options:
         - userID: User ID or username. If no userID provided, current login user's
                   permissions will be listed."""
         user_info = get_user(userID, strict=True)
-        return get_user_perms(user_info['id'])
+        return get_user_perms(user_info['id'], with_groups=with_groups)
 
     def getAllPerms(self):
         """Get a list of all permissions in the system.  Returns a list of maps.  Each
diff --git a/tests/test_cli/test_list_permissions.py b/tests/test_cli/test_list_permissions.py
index 4f0afd1..fbb0224 100644
--- a/tests/test_cli/test_list_permissions.py
+++ b/tests/test_cli/test_list_permissions.py
@@ -85,7 +85,7 @@ class TestListPermissions(utils.CliTestCase):
 
         self.activate_session_mock.assert_called_once()
         self.session.getUser.assert_called_once()
-        self.session.getUserPerms.assert_called_once()
+        self.session.getUserPerms.assert_called()
         self.session.getPerms.assert_not_called()
         self.session.getAllPerms.assert_not_called()
 
@@ -108,7 +108,7 @@ repo
 
         self.activate_session_mock.assert_called_once()
         self.session.getUser.assert_called_once()
-        self.session.getUserPerms.assert_called_once()
+        self.session.getUserPerms.assert_called()
         self.session.getPerms.assert_not_called()
         self.session.getAllPerms.assert_not_called()
 
@@ -119,14 +119,15 @@ repo
 repo              
 """
         perms = [p['name'] for p in self.all_perms[1:3]]
-        self.session.getPerms.return_value = perms
+        self.session.getLoggedInUser.return_value = {'id': 1, 'name': 'user'}
+        self.session.getUserPerms.return_value = perms
         handle_list_permissions(self.options, self.session, ['--mine'])
         self.assert_console_message(stdout, expected)
 
         self.activate_session_mock.assert_called_once()
-        self.session.getUser.assert_not_called()
-        self.session.getUserPerms.assert_not_called()
-        self.session.getPerms.assert_called_once()
+        self.session.getUser.assert_called_once()
+        self.session.getUserPerms.assert_called()
+        self.session.getPerms.assert_not_called()
         self.session.getAllPerms.assert_not_called()
 
     @mock.patch('sys.stdout', new_callable=six.StringIO)
diff --git a/tests/test_hub/test_get_user_perms.py b/tests/test_hub/test_get_user_perms.py
index 948f22a..52355e3 100644
--- a/tests/test_hub/test_get_user_perms.py
+++ b/tests/test_hub/test_get_user_perms.py
@@ -21,4 +21,4 @@ class TestGetUserPerms(unittest.TestCase):
     def test_normal(self):
         self.get_user.return_value = {'id': 123, 'name': 'testuser'}
         kojihub.RootExports().getUserPerms(123)
-        self.get_user_perms.assert_called_once_with(123)
+        self.get_user_perms.assert_called_once_with(123, with_groups=True)
diff --git a/tests/test_lib/test_auth.py b/tests/test_lib/test_auth.py
index 6735664..6428ec4 100644
--- a/tests/test_lib/test_auth.py
+++ b/tests/test_lib/test_auth.py
@@ -703,19 +703,37 @@ class TestAuthSession(unittest.TestCase):
         query = self.queries[0]
         self.assertEqual(query.tables, ['user_groups'])
         self.assertEqual(query.joins, ['users ON group_id = users.id'])
-        self.assertEqual(query.clauses, ['active = TRUE', 'user_id=%(user_id)i',
+        self.assertEqual(query.clauses, ['active IS TRUE', 'user_id=%(user_id)i',
                                          'users.usertype=%(t_group)i'])
         self.assertEqual(query.columns, ['group_id', 'name'])
 
     def test_get_user_perms(self):
         """auth.get_user_perms"""
         kojihub.auth.get_user_perms(1)
-        self.assertEqual(len(self.queries), 1)
+        self.assertEqual(len(self.queries), 2)
         query = self.queries[0]
         self.assertEqual(query.tables, ['user_perms'])
         self.assertEqual(query.joins, ['permissions ON perm_id = permissions.id'])
-        self.assertEqual(query.clauses, ['active = TRUE', 'user_id=%(user_id)s'])
+        self.assertEqual(query.clauses, ['active IS TRUE', 'user_id=%(user_id)s'])
         self.assertEqual(query.columns, ['name'])
+        query = self.queries[1]
+        self.assertEqual(query.tables, ['user_groups'])
+        self.assertEqual(query.joins, [
+            'LEFT JOIN user_perms ON user_perms.user_id = user_groups.group_id',
+            'permissions ON perm_id = permissions.id'])
+        self.assertEqual(sorted(query.clauses), sorted([
+            'user_groups.active IS TRUE',
+            'user_perms.active IS TRUE',
+            'user_groups.user_id=%(user_id)s']))
+        self.assertEqual(query.columns, ['name'])
+
+    def test_get_user_perms_inherited(self):
+        self.query_execute.side_effect = [
+            [{'id': 1, 'name': 'perm1'}, {'id': 2, 'name': 'perm2'}],
+            [{'id': 3, 'name': 'perm3'}]
+        ]
+        result = kojihub.auth.get_user_perms(1)
+        self.assertEqual(set(result), {'perm1', 'perm2', 'perm3'})
 
     def test_logout_logged_not_owner(self):
         s, _ = self.get_session()

From bf9060ab682d9dc7b09daee8eb2fe6e10eb37c03 Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Aug 28 2023 07:18:14 +0000
Subject: [PATCH 2/5] fix sanity checks in revoke/grantPermission


---

diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index 3e07748..93d2739 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -12668,7 +12668,7 @@ class RootExports(object):
                                      values={'perm_id': perm_id})
             update.set(description=description)
             update.execute()
-        if perm['name'] in get_user_perms(user_id):
+        if perm['name'] in get_user_perms(user_id, with_groups=False):
             raise koji.GenericError('user %s already has permission: %s' %
                                     (userinfo, perm['name']))
         insert = InsertProcessor('user_perms')
@@ -12682,7 +12682,7 @@ class RootExports(object):
         user_id = get_user(userinfo, strict=True)['id']
         perm = lookup_perm(permission, strict=True)
         perm_id = perm['id']
-        if perm['name'] not in get_user_perms(user_id):
+        if perm['name'] not in get_user_perms(user_id, with_groups=False):
             raise koji.GenericError('user %s does not have permission: %s' %
                                     (userinfo, perm['name']))
         update = UpdateProcessor('user_perms', values=locals(),

From e74d8f91fdc4fce0108e8ae856c5dae573f8129d Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Aug 28 2023 07:18:14 +0000
Subject: [PATCH 3/5] fix query


---

diff --git a/kojihub/auth.py b/kojihub/auth.py
index 5ea2ded..cdd3d2b 100644
--- a/kojihub/auth.py
+++ b/kojihub/auth.py
@@ -799,8 +799,7 @@ def get_user_perms(user_id, with_groups=True):
                                    'user_perms.active IS TRUE',
                                    'user_groups.user_id=%(user_id)s'],
                                joins=[
-                                   'LEFT JOIN user_perms ON '
-                                   'user_perms.user_id = user_groups.group_id',
+                                   'user_perms ON user_perms.user_id = user_groups.group_id',
                                    'permissions ON perm_id = permissions.id'],
                                values={'user_id': user_id})
         result = query.execute()

From 87e33e4926ec6ace015909480fdd7a081cd7c80a Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Aug 28 2023 07:18:14 +0000
Subject: [PATCH 4/5] getUserPermsInheritance


---

diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index 93d2739..594ad87 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -13390,6 +13390,34 @@ class RootExports(object):
         user_info = get_user(userID, strict=True)
         return get_user_perms(user_info['id'], with_groups=with_groups)
 
+    def getUserPermsInheritance(self, userID):
+        """Get a dict of the permissions granted directly to user or inherited from groups
+        with the sources.
+
+        :param int userID: User id
+        :returns dict[str, list[str]]: list of permissions with source (None/group)
+        """
+        user_info = get_user(userID, strict=True)
+        perms = {}
+        for perm in get_user_perms(user_info['id'], with_groups=False):
+            perms[perm] = [None]
+
+        query = QueryProcessor(tables=['user_groups'],
+                               columns=['permissions.name', 'users.name'],
+                               aliases=['permission', 'group'],
+                               clauses=[
+                                   'user_groups.active IS TRUE',
+                                   'user_perms.active IS TRUE',
+                                   'user_groups.user_id=%(user_id)s'],
+                               joins=[
+                                   'user_perms ON user_perms.user_id = user_groups.group_id',
+                                   'permissions ON perm_id = permissions.id',
+                                   'users ON user_groups.group_id = users.id'],
+                               values={'user_id': user_info['id']})
+        for row in query.execute():
+            perms.setdefault(row['permission'], []).append(row['group'])
+        return perms
+
     def getAllPerms(self):
         """Get a list of all permissions in the system.  Returns a list of maps.  Each
         map contains the following keys:
diff --git a/tests/test_hub/test_get_user_perms.py b/tests/test_hub/test_get_user_perms.py
index 52355e3..9af6bf0 100644
--- a/tests/test_hub/test_get_user_perms.py
+++ b/tests/test_hub/test_get_user_perms.py
@@ -1,6 +1,7 @@
 import mock
 import unittest
 import koji
+from .utils import DBQueryTestCase
 import kojihub
 
 
@@ -22,3 +23,36 @@ class TestGetUserPerms(unittest.TestCase):
         self.get_user.return_value = {'id': 123, 'name': 'testuser'}
         kojihub.RootExports().getUserPerms(123)
         self.get_user_perms.assert_called_once_with(123, with_groups=True)
+
+
+class TestGetUserPermsInheritance(DBQueryTestCase):
+    def setUp(self):
+        super(TestGetUserPermsInheritance, self).setUp()
+        self.get_user = mock.patch('kojihub.kojihub.get_user').start()
+        self.get_user_perms = mock.patch('kojihub.kojihub.get_user_perms').start()
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_no_user(self):
+        self.get_user.side_effect = koji.GenericError
+        with self.assertRaises(koji.GenericError):
+            kojihub.RootExports().getUserPermsInheritance(123)
+        self.get_user_perms.assert_not_called()
+
+    def test_normal(self):
+        self.get_user.return_value = {'id': 123, 'name': 'testuser'}
+        self.get_user_perms.return_value = ['test1', 'test2']
+        self.qp_execute_return_value = [
+            {'permission': 'test2', 'group': 'group1'},
+            {'permission': 'test3', 'group': 'group1'},
+            {'permission': 'test3', 'group': 'group2'},
+        ]
+        result = kojihub.RootExports().getUserPermsInheritance(123)
+        self.assertEqual(result, {
+            'test1': [None],
+            'test2': [None, 'group1'],
+            'test3': ['group1', 'group2'],
+        })
+        self.get_user.assert_called_once_with(123, strict=True)
+        self.get_user_perms.assert_called_once_with(123, with_groups=False)
diff --git a/tests/test_lib/test_auth.py b/tests/test_lib/test_auth.py
index 6428ec4..3ac4fef 100644
--- a/tests/test_lib/test_auth.py
+++ b/tests/test_lib/test_auth.py
@@ -719,7 +719,7 @@ class TestAuthSession(unittest.TestCase):
         query = self.queries[1]
         self.assertEqual(query.tables, ['user_groups'])
         self.assertEqual(query.joins, [
-            'LEFT JOIN user_perms ON user_perms.user_id = user_groups.group_id',
+            'user_perms ON user_perms.user_id = user_groups.group_id',
             'permissions ON perm_id = permissions.id'])
         self.assertEqual(sorted(query.clauses), sorted([
             'user_groups.active IS TRUE',

From c0f25ad09892e313e926443a2703d00eb36a71a9 Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Aug 28 2023 07:18:14 +0000
Subject: [PATCH 5/5] refactor get_user_perms


---

diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py
index adb927f..7a3c7a8 100644
--- a/cli/koji_cli/commands.py
+++ b/cli/koji_cli/commands.py
@@ -2174,12 +2174,10 @@ def handle_list_permissions(goptions, session, args):
         user = session.getUser(options.user)
         if not user:
             error("No such user: %s" % options.user)
-        all = set(session.getUserPerms(user['id']))
-        own = set(session.getUserPerms(user['id'], with_groups=False))
-        for p in all:
+        for p, groups in session.getUserPermsInheritance(user['id']).items():
             p = {'name': p}
-            if p['name'] not in own:
-                p['description'] = '[inherited]'
+            if groups != [None]:
+                p['description'] = 'inherited from: %s' % ', '.join(groups)
             perms.append(p)
     else:
         for p in session.getAllPerms():
diff --git a/kojihub/auth.py b/kojihub/auth.py
index cdd3d2b..cd774ce 100644
--- a/kojihub/auth.py
+++ b/kojihub/auth.py
@@ -782,29 +782,61 @@ def get_user_groups(user_id):
     return groups
 
 
-def get_user_perms(user_id, with_groups=True):
+def get_user_perms(user_id, with_groups=True, inheritance_data=False):
+    """
+    :param int user_id: User ID
+    :param bool with_groups: Add also permissions from all groups and their inheritance chain
+    :param bool inheritance_data: Return extended data about permissions sources
+    :returns list[str]: in case of inheritance_data=False
+    :returns dict[str, list[str]]: in case of inheritance_data=True - keys are permissions' names,
+                                   values list of groups which are in inheritance and provides
+                                   given permission.
+    """
+    if inheritance_data and not with_groups:
+        raise koji.ParameterError("inheritance option implies with_groups")
+
     # individual permissions
+    perms = {}
     query = QueryProcessor(tables=['user_perms'], columns=['name'],
                            clauses=['active IS TRUE', 'user_id=%(user_id)s'],
                            joins=['permissions ON perm_id = permissions.id'],
                            values={'user_id': user_id})
-    result = query.execute()
-    perms = {r['name'] for r in result}
+    for perm in query.execute():
+        perms[perm['name']] = [None]
 
-    # inherited group permissions
     if with_groups:
-        query = QueryProcessor(tables=['user_groups'], columns=['name'],
+        columns = ['permissions.name']
+        aliases = ['name']
+        joins = [
+            'user_perms ON user_perms.user_id = user_groups.group_id',
+            'permissions ON perm_id = permissions.id',
+        ]
+        if inheritance_data:
+            # inheritance data adds one more join and as function
+            # can be called relatively often (e.g. in hub policy tests)
+            # it is a bit faster to ignore this join for "default" code path
+            columns.append('users.name')
+            aliases.append('group')
+            joins.append('users ON user_groups.group_id = users.id')
+        query = QueryProcessor(tables=['user_groups'],
+                               columns=columns,
+                               aliases=aliases,
                                clauses=[
                                    'user_groups.active IS TRUE',
                                    'user_perms.active IS TRUE',
                                    'user_groups.user_id=%(user_id)s'],
-                               joins=[
-                                   'user_perms ON user_perms.user_id = user_groups.group_id',
-                                   'permissions ON perm_id = permissions.id'],
+                               joins=joins,
                                values={'user_id': user_id})
-        result = query.execute()
-        perms |= {r['name'] for r in result}
-    return list(perms)
+        for row in query.execute():
+            if inheritance_data:
+                perms.setdefault(row['name'], []).append(row['group'])
+            else:
+                # group name wouldn't be used in this case
+                perms.setdefault(row['name'], [])
+    if inheritance_data:
+        return perms
+    else:
+        return list(perms.keys())
 
 
 def get_user_data(user_id):
diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index 594ad87..d103a0d 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -13388,7 +13388,7 @@ class RootExports(object):
         - userID: User ID or username. If no userID provided, current login user's
                   permissions will be listed."""
         user_info = get_user(userID, strict=True)
-        return get_user_perms(user_info['id'], with_groups=with_groups)
+        return get_user_perms(user_info['id'], with_groups=with_groups, inheritance_data=False)
 
     def getUserPermsInheritance(self, userID):
         """Get a dict of the permissions granted directly to user or inherited from groups
@@ -13398,25 +13398,7 @@ class RootExports(object):
         :returns dict[str, list[str]]: list of permissions with source (None/group)
         """
         user_info = get_user(userID, strict=True)
-        perms = {}
-        for perm in get_user_perms(user_info['id'], with_groups=False):
-            perms[perm] = [None]
-
-        query = QueryProcessor(tables=['user_groups'],
-                               columns=['permissions.name', 'users.name'],
-                               aliases=['permission', 'group'],
-                               clauses=[
-                                   'user_groups.active IS TRUE',
-                                   'user_perms.active IS TRUE',
-                                   'user_groups.user_id=%(user_id)s'],
-                               joins=[
-                                   'user_perms ON user_perms.user_id = user_groups.group_id',
-                                   'permissions ON perm_id = permissions.id',
-                                   'users ON user_groups.group_id = users.id'],
-                               values={'user_id': user_info['id']})
-        for row in query.execute():
-            perms.setdefault(row['permission'], []).append(row['group'])
-        return perms
+        return get_user_perms(user_info['id'], inheritance_data=True)
 
     def getAllPerms(self):
         """Get a list of all permissions in the system.  Returns a list of maps.  Each
diff --git a/tests/test_cli/test_list_permissions.py b/tests/test_cli/test_list_permissions.py
index fbb0224..2b8a0b0 100644
--- a/tests/test_cli/test_list_permissions.py
+++ b/tests/test_cli/test_list_permissions.py
@@ -79,13 +79,14 @@ class TestListPermissions(utils.CliTestCase):
 """
         self.options.quiet = False
         self.session.getUser.return_value = self.userinfo
-        self.session.getUserPerms.return_value = []
+        self.session.getUserPermsInheritance.return_value = {}
         handle_list_permissions(self.options, self.session, ['--user', self.user])
         self.assert_console_message(stdout, expected)
 
         self.activate_session_mock.assert_called_once()
         self.session.getUser.assert_called_once()
-        self.session.getUserPerms.assert_called()
+        self.session.getUserPerms.assert_not_called()
+        self.session.getUserPermsInheritance.assert_called_once()
         self.session.getPerms.assert_not_called()
         self.session.getAllPerms.assert_not_called()
 
@@ -100,15 +101,16 @@ livecd
 long-permission-appliance
 repo                     
 """
-        perms = [p['name'] for p in self.all_perms[::1]]
-        self.session.getUserPerms.return_value = perms
+        perms = {p['name']: [None] for p in self.all_perms[::1]}
+        self.session.getUserPermsInheritance.return_value = perms
         self.session.getUser.return_value = self.userinfo
         handle_list_permissions(self.options, self.session, ['--user', self.user])
         self.assert_console_message(stdout, expected)
 
         self.activate_session_mock.assert_called_once()
         self.session.getUser.assert_called_once()
-        self.session.getUserPerms.assert_called()
+        self.session.getUserPerms.assert_not_called()
+        self.session.getUserPermsInheritance.assert_called_once_with(self.userinfo['id'])
         self.session.getPerms.assert_not_called()
         self.session.getAllPerms.assert_not_called()
 
@@ -118,15 +120,16 @@ repo
         expected = """build             
 repo              
 """
-        perms = [p['name'] for p in self.all_perms[1:3]]
+        perms = {p['name']: [None] for p in self.all_perms[1:3]}
         self.session.getLoggedInUser.return_value = {'id': 1, 'name': 'user'}
-        self.session.getUserPerms.return_value = perms
+        self.session.getUserPermsInheritance.return_value = perms
         handle_list_permissions(self.options, self.session, ['--mine'])
         self.assert_console_message(stdout, expected)
 
         self.activate_session_mock.assert_called_once()
         self.session.getUser.assert_called_once()
-        self.session.getUserPerms.assert_called()
+        self.session.getUserPerms.assert_not_called()
+        self.session.getUserPermsInheritance.assert_called_once()
         self.session.getPerms.assert_not_called()
         self.session.getAllPerms.assert_not_called()
 
diff --git a/tests/test_hub/test_get_user_perms.py b/tests/test_hub/test_get_user_perms.py
index 9af6bf0..35aba52 100644
--- a/tests/test_hub/test_get_user_perms.py
+++ b/tests/test_hub/test_get_user_perms.py
@@ -3,6 +3,7 @@ import unittest
 import koji
 from .utils import DBQueryTestCase
 import kojihub
+from kojihub.auth import get_user_perms
 
 
 class TestGetUserPerms(unittest.TestCase):
@@ -22,7 +23,7 @@ class TestGetUserPerms(unittest.TestCase):
     def test_normal(self):
         self.get_user.return_value = {'id': 123, 'name': 'testuser'}
         kojihub.RootExports().getUserPerms(123)
-        self.get_user_perms.assert_called_once_with(123, with_groups=True)
+        self.get_user_perms.assert_called_once_with(123, with_groups=True, inheritance_data=False)
 
 
 class TestGetUserPermsInheritance(DBQueryTestCase):
@@ -41,18 +42,14 @@ class TestGetUserPermsInheritance(DBQueryTestCase):
         self.get_user_perms.assert_not_called()
 
     def test_normal(self):
-        self.get_user.return_value = {'id': 123, 'name': 'testuser'}
-        self.get_user_perms.return_value = ['test1', 'test2']
-        self.qp_execute_return_value = [
-            {'permission': 'test2', 'group': 'group1'},
-            {'permission': 'test3', 'group': 'group1'},
-            {'permission': 'test3', 'group': 'group2'},
-        ]
-        result = kojihub.RootExports().getUserPermsInheritance(123)
-        self.assertEqual(result, {
+        data = {
             'test1': [None],
             'test2': [None, 'group1'],
             'test3': ['group1', 'group2'],
-        })
+        }
+        self.get_user.return_value = {'id': 123, 'name': 'testuser'}
+        self.get_user_perms.return_value = data
+        result = kojihub.RootExports().getUserPermsInheritance(123)
+        self.assertEqual(result, data)
         self.get_user.assert_called_once_with(123, strict=True)
-        self.get_user_perms.assert_called_once_with(123, with_groups=False)
+        self.get_user_perms.assert_called_once_with(123, inheritance_data=True)
diff --git a/tests/test_lib/test_auth.py b/tests/test_lib/test_auth.py
index 3ac4fef..a8672df 100644
--- a/tests/test_lib/test_auth.py
+++ b/tests/test_lib/test_auth.py
@@ -725,16 +725,31 @@ class TestAuthSession(unittest.TestCase):
             'user_groups.active IS TRUE',
             'user_perms.active IS TRUE',
             'user_groups.user_id=%(user_id)s']))
-        self.assertEqual(query.columns, ['name'])
+        self.assertEqual(query.columns, ['permissions.name'])
 
     def test_get_user_perms_inherited(self):
         self.query_execute.side_effect = [
             [{'id': 1, 'name': 'perm1'}, {'id': 2, 'name': 'perm2'}],
-            [{'id': 3, 'name': 'perm3'}]
+            [{'name': 'perm3'}]
         ]
         result = kojihub.auth.get_user_perms(1)
         self.assertEqual(set(result), {'perm1', 'perm2', 'perm3'})
 
+    def test_get_user_perms_inherited_data(self):
+        self.query_execute.side_effect = [
+            [{'id': 1, 'name': 'perm1'}, {'id': 2, 'name': 'perm2'}],
+            [{'name': 'perm3', 'group': 'group_a'},
+             {'name': 'perm4', 'group': 'group_b'},
+             {'name': 'perm4', 'group': 'group_c'}]
+        ]
+        result = kojihub.auth.get_user_perms(1, inheritance_data=True)
+        self.assertEqual(result, {
+            'perm1': [None],
+            'perm2': [None],
+            'perm3': ['group_a'],
+            'perm4': ['group_b', 'group_c'],
+        })
+
     def test_logout_logged_not_owner(self):
         s, _ = self.get_session()
 

