From d79463e9605fcb1bef250ab3e9cf717227fe04ed Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+studip@gmail.com>
Date: Wed, 22 Mar 2023 12:27:53 +0000
Subject: [PATCH] implement RolePersistence::getUsersWithRoleByName() and
 RolePersistence::getUsersWithRoleById(), fixes #2014

Closes #2014

Merge request studip/studip!1311
---
 app/controllers/admin/role.php           | 24 +++++-----
 app/views/admin/role/show_role.php       | 24 +++++-----
 lib/plugins/db/RolePersistence.class.php | 56 ++++++++++++++++++++++++
 3 files changed, 80 insertions(+), 24 deletions(-)

diff --git a/app/controllers/admin/role.php b/app/controllers/admin/role.php
index 513bcbd9c78..c316021cd88 100644
--- a/app/controllers/admin/role.php
+++ b/app/controllers/admin/role.php
@@ -288,18 +288,17 @@ class Admin_RoleController extends AuthenticatedController
         $this->roleid = '';
 
         if ($roleid) {
-            $sql = "SELECT DISTINCT Vorname,Nachname,user_id,username,perms
-                    FROM auth_user_md5
-                    JOIN roles_user ON userid = user_id
-                    WHERE roleid = ?
-                    ORDER BY Nachname, Vorname";
-            $statement = DBManager::get()->prepare($sql);
-            $statement->execute([$roleid]);
-
-            $users = $statement->fetchAll(PDO::FETCH_ASSOC);
-            foreach ($users as $key => $user) {
-                $institutes = new SimpleCollection(Institute::findMany(RolePersistence::getAssignedRoleInstitutes($user['user_id'], $roleid)));
-                $users[$key]['institutes'] = $institutes->orderBy('name')->pluck('name');
+            $this->users = RolePersistence::getUsersWithRoleById($roleid);
+
+            $this->user_institutes = [];
+            foreach ($this->users as $user) {
+                $this->user_institutes[$user->id] = Institute::findAndMapMany(
+                    function (Institute $institute) {
+                        return $institute->name;
+                    },
+                    RolePersistence::getAssignedRoleInstitutes($user['user_id'], $roleid),
+                    'ORDER BY name'
+                );
             }
 
             $plugins = PluginManager::getInstance()->getPluginInfos();
@@ -311,7 +310,6 @@ class Admin_RoleController extends AuthenticatedController
 
             $this->implicit_count = RolePersistence::countImplicitUsers($roleid);
 
-            $this->users   = $users;
             $this->plugins = $plugins;
             $this->role    = self::getRole($roleid);
             $this->roleid  = $roleid;
diff --git a/app/views/admin/role/show_role.php b/app/views/admin/role/show_role.php
index e01a952cdb3..196f9bf20b6 100644
--- a/app/views/admin/role/show_role.php
+++ b/app/views/admin/role/show_role.php
@@ -5,7 +5,8 @@
  * @var string $roleid
  * @var Role[] $roles
  * @var QuickSearch $mps
- * @var array $users
+ * @var User[] $users
+ * @var array $user_institutes
  * @var array $plugins
  * @var int $implicit_count
  */
@@ -90,29 +91,30 @@ use Studip\Button;
         <? foreach (array_values($users) as $index => $user): ?>
             <tr>
                 <td>
-                    <input type="checkbox" name="ids[]" value="<?= $user['user_id'] ?>">
+                    <input type="checkbox" name="ids[]" value="<?= htmlReady($user->id) ?>">
                 </td>
                 <td style="text-align: right;">
                     <?= $index + 1 ?>.
                 </td>
                 <td>
-                    <a href="<?= $controller->url_for('admin/role/assign_role', $user['user_id']) ?>">
-                        <?= htmlReady(sprintf('%s %s (%s)', $user['Vorname'], $user['Nachname'], $user['username'])) ?>
+                    <a href="<?= $controller->link_for('admin/role/assign_role', $user->id) ?>">
+                        <?= htmlReady(sprintf('%s %s (%s)', $user->vorname, $user->nachname, $user->username)) ?>
                     </a>
                 </td>
-                <td><?= $user['perms'] ?></td>
+                <td><?= htmlReady($user->perms) ?></td>
                 <td>
-                <? $institutes = join(', ', $user['institutes']); ?>
+                <? $institutes = join(', ', $user_institutes[$user->id]); ?>
                     <?= htmlReady(mb_substr($institutes, 0, 60)) ?>
                     <? if (mb_strlen($institutes) > 60): ?>
-                    ...<?= tooltipIcon(join("\n", $user['institutes']))?>
+                    ...<?= tooltipIcon(join("\n", $user_institutes[$user->id]))?>
                     <? endif ?>
                 </td>
                 <td class="actions">
-                    <?= Icon::create('trash', 'clickable', ['title' => _('Rolle entziehen')])
-                            ->asInput([
-                                "data-confirm" => _('Soll dieser Person wirklich die Rolle entzogen werden?'),
-                                "formaction" => $controller->url_for('admin/role/remove_user/'.$roleid.'/'.$user['user_id'])]) ?>
+                    <?= Icon::create('trash')->asInput([
+                        'title'        => _('Rolle entziehen'),
+                        'data-confirm' => _('Soll dieser Person wirklich die Rolle entzogen werden?'),
+                        'formaction'   => $controller->url_for('admin/role/remove_user', $roleid, $user->id),
+                    ]) ?>
                 </td>
             </tr>
         <? endforeach; ?>
diff --git a/lib/plugins/db/RolePersistence.class.php b/lib/plugins/db/RolePersistence.class.php
index 3514ca6c6d3..85f47a2a17b 100644
--- a/lib/plugins/db/RolePersistence.class.php
+++ b/lib/plugins/db/RolePersistence.class.php
@@ -525,6 +525,62 @@ class RolePersistence
         ));
     }
 
+    /**
+     * Returns all users that have a specific role - given by it's name.
+     *
+     * @param string $role_name   Name of the role
+     * @param bool   $only_explicit Only select explicit assignments from table
+     *                              `roles_user` if true, otherwise also select
+     *                              by perm defined in table `roles_studipperms`
+     *
+     * @return User[]
+     */
+    public static function getUsersWithRoleByName(string $role_name, bool $only_explicit = true): array
+    {
+        $role_id = self::getRoleIdByName($role_name);
+        if ($role_id === false) {
+            throw new Exception("Unknown role name {$role_name}");
+        }
+
+        return self::getUsersWithRoleById($role_id, $only_explicit);
+    }
+
+    /**
+     * Returns all users that have a specific role - given by it's id.
+     *
+     * @param int  $role_id       Id of the role
+     * @param bool $only_explicit Only select explicit assignments from table
+     *                            `roles_user` if true, otherwise also select
+     *                            by perm defined in table `roles_studipperms`
+     *
+     * @return User[]
+     */
+    public static function getUsersWithRoleById(int $role_id, bool $only_explicit = true): array
+    {
+        $query = "SELECT `userid` AS `user_id`
+                  FROM `roles_user`
+                  WHERE `roleid` = :role_id";
+
+        if (!$only_explicit) {
+            $query = "SELECT DISTINCT `user_id`
+                      FROM (
+                          {$query}
+
+                          UNION ALL
+
+                          SELECT `user_id`
+                          FROM `roles_studipperms` AS `rsp`
+                          JOIN `auth_user_md5` AS `aum`
+                            ON (`rsp`.`permname` = `aum`.`perms`)
+                          WHERE `rsp`.`roleid` = :role_id
+                      ) AS tmp";
+        }
+
+        $user_ids = DBManager::get()->fetchFirst($query, [':role_id' => $role_id]);
+
+        return User::findMany($user_ids);
+    }
+
     /**
      * Returns statistic values for each role:
      *
-- 
GitLab