瀏覽代碼

add the resetBeUsersPerms method, implements new be_users structure

Olivier Massot 4 年之前
父節點
當前提交
2ddddaae48

+ 83 - 0
ot_admin/Classes/Command/ResetBeUserPermsCommand.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace Opentalent\OtAdmin\Command;
+
+
+use Opentalent\OtAdmin\Controller\SiteController;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Object\ObjectManager;
+
+/**
+ * This CLI command resets the permissions granted to
+ * this website be users
+ *
+ * @package Opentalent\OtAdmin\Command
+ */
+class ResetBeUserPermsCommand extends Command
+{
+    /**
+     * -- This method is expected by Typo3, do not rename ou remove --
+     *
+     * Allows to configure the command.
+     * Allows to add a description, a help text, and / or define arguments.
+     *
+     */
+    protected function configure()
+    {
+        $this
+            ->setName("ot:site:reset-perms")
+            ->setDescription("Reset the permissions granted to the be users")
+            ->setHelp("This CLI command resets the permissions granted to 
+                            either one website be users, or every websites be users")
+            ->addArgument(
+                'organization-id',
+                InputArgument::OPTIONAL,
+                "The organization's id in the opentalent DB"
+            )
+            ->addOption(
+                "all",
+                null,
+                InputOption::VALUE_NONE,
+                "Reset all of the websites be_users permissions."
+            );
+    }
+
+    /**
+     * -- This method is expected by Typo3, do not rename ou remove --
+     *
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     * @throws \Exception
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $org_id = $input->getArgument('organization-id');
+        $all = $input->getOption('all');
+
+        if ($all && $org_id) {
+            throw new \InvalidArgumentException("You can not pass both an organization id and the --all option");
+        }
+        if (!$all && !$org_id) {
+            throw new \InvalidArgumentException("You shall either pass an organization id or use the --all option");
+        }
+
+        $io = new SymfonyStyle($input, $output);
+
+        $siteController = GeneralUtility::makeInstance(ObjectManager::class)->get(SiteController::class);
+
+        if ($all) {
+            $siteController->resetAllBeUserPermsAction();
+            $io->success(sprintf("Be users permissions were reset for every website"));
+        } else {
+            $rootUid = $siteController->resetBeUserPermsAction($org_id);
+            $io->success(sprintf("The website with root uid " . $rootUid . " had its be users permissions reset"));
+        }
+    }
+
+}

+ 389 - 31
ot_admin/Classes/Controller/SiteController.php

@@ -58,14 +58,33 @@ class SiteController extends ActionController
     const DEFAULT_COLOR = 'light-blue';
 
     // BE rights
-    const PRODUCT_MAPPING = [
-        "school-standard" => 1, // Association writer basic
-        "artist-standard" => 1, // Association writer basic
-        "school-premium" => 3, // Association writer full
-        "artist-premium" => 3, // Association writer full
-        "manager" => 3, // Association writer full
+    const BEGROUP_EDITOR_STANDARD = 10;
+    const BEGROUP_EDITOR_PREMIUM = 20;
+    const BEGROUP_ADMIN_STANDARD = 30;
+    const BEGROUP_ADMIN_PREMIUM = 40;
+
+    const BEGROUP_NAME = [
+        self::BEGROUP_EDITOR_STANDARD => "EditorStandard",
+        self::BEGROUP_EDITOR_PREMIUM => "EditorPremium",
+        self::BEGROUP_ADMIN_STANDARD => "AdminStandard",
+        self::BEGROUP_ADMIN_PREMIUM => "AdminPremium"
     ];
 
+    const IS_PRODUCT_PREMIUM = [
+        "school-standard" => false,
+        "artist-standard" => false,
+        "school-premium" => true,
+        "artist-premium" => true,
+        "manager" => true,
+    ];
+
+    // access permissions
+    const PERM_SHOW = 1;
+    const PERM_EDIT_CONTENT = 16;
+    const PERM_EDIT_PAGE = 2;
+    const PERM_DELETE = 4;
+    const PERM_NEW = 8;
+
     // Creation mode
     const MODE_PROD = 1;
     const MODE_DEV = 1;
@@ -136,7 +155,7 @@ class SiteController extends ActionController
     {
         $rootUid = $this->findRootUidFor($organizationId);
         $config = $this->findConfigFor($organizationId, $rootUid);
-        //$extraData = $this->fetchOrganizationExtraData($organizationId);
+        $organizationExtraData = $this->fetchOrganizationExtraData($organizationId);
 
         $rootPage = $this->otPageRepository->getPage($rootUid);
 
@@ -147,12 +166,17 @@ class SiteController extends ActionController
             $rootPage['tx_opentalent_template'],
             $rootPage['tx_opentalent_template_preferences'],
             $rootPage['tx_opentalent_matomo_id'],
+            self::IS_PRODUCT_PREMIUM[$organizationExtraData['admin']['product']] ?? false,
             (bool)$rootPage['deleted'],
-            (bool)($rootPage['hidden'] || $rootPage['fe_group'] < 0)
+            (bool)($rootPage['hidden'] || $rootPage['fe_group'] < 0),
+            null,
+            null,
+            $rootPage['perms_userid'],
+            $rootPage['perms_groupid']
         );
 
         // Owners
-        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_groups');
         $queryBuilder->getRestrictions()->removeAll();
         $beUsers = $queryBuilder
             ->select('uid', 'username')
@@ -165,6 +189,19 @@ class SiteController extends ActionController
             $site->addMountedForBeUser($beUser);
         }
 
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_groups');
+        $queryBuilder->getRestrictions()->removeAll();
+        $beGroups = $queryBuilder
+            ->select('uid', 'title')
+            ->from('be_groups')
+            ->where('FIND_IN_SET(' . $rootUid . ', db_mountpoints) > 0')
+            ->execute()
+            ->fetchAll();
+
+        foreach ($beGroups as $beGroup) {
+            $site->addMountedForBeGroups($beGroup);
+        }
+
         return $site;
     }
 
@@ -512,11 +549,21 @@ class SiteController extends ActionController
                 ])
                 ->execute();
 
+            // Create the BE Editors group
+            // -- NB: this user will then be auto-updated by the ot_connect extension --
+            $beGroupUid = $this->createBeGroup(
+                $organizationId,
+                $rootUid,
+                $domain,
+                $organizationExtraData['admin']
+            );
+
             // Create the BE User
             // -- NB: this user will then be auto-updated by the ot_connect extension --
             $beUserUid = $this->createBeUser(
                 $organizationId,
                 $rootUid,
+                $beGroupUid,
                 $domain,
                 $organizationExtraData['admin']
             );
@@ -531,24 +578,27 @@ class SiteController extends ActionController
                 ->set('TSconfig', $tsconfig)
                 ->execute();
 
-            // Give the keys of the website to this user (makes him the owner)
+            // Give the keys of the website to this user and group (makes him the owner)
             $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
             foreach($this->createdPagesIndex as $slug => $uid) {
                 $queryBuilder
                     ->update('pages')
                     ->where($queryBuilder->expr()->eq('uid', $uid))
                     ->set('perms_userid', $beUserUid)
+                    ->set('perms_groupid', $beGroupUid)
                     ->execute();
             }
 
+            // Setup user and group rights
+            $this->setBeUserPerms($organizationId);
+
             // Try to commit the result
             $commitSuccess = $this->connectionPool->getConnectionByName('Default')->commit();
             if (!$commitSuccess) {
                 throw new \RuntimeException('Something went wrong while commiting the result');
             }
 
-
-        } catch(\Exception $e) {
+        } catch(\Throwable $e) {
             // rollback
             $this->connectionPool->getConnectionByName('Default')->rollback();
 
@@ -790,7 +840,7 @@ class SiteController extends ActionController
             }
             return $rootUid;
 
-        } catch(\Exception $e) {
+        } catch(\Throwable $e) {
             // rollback
             $this->connectionPool->getConnectionByName('Default')->rollback();
             if (!$hard) {
@@ -798,10 +848,11 @@ class SiteController extends ActionController
                     rename($newPath, $initialPath);
                 }
             }
+            throw $e;
+        } finally {
             if (file_exists($confirm_file)) {
                 unlink($confirm_file);
             }
-            throw $e;
         }
     }
 
@@ -958,7 +1009,7 @@ class SiteController extends ActionController
             }
             return $rootUid;
 
-        } catch(\Exception $e) {
+        } catch(\Throwable $e) {
             // rollback
             $this->connectionPool->getConnectionByName('Default')->rollback();
             foreach ($renamed as $initialPath => $newPath) {
@@ -1334,6 +1385,157 @@ class SiteController extends ActionController
         return $deleted;
     }
 
+    /**
+     * Set the rights of admin and editors of the website
+     * on all of the existing pages, including deleted ones
+     *
+     * @param int $organizationId
+     * @param int|null $editorsGroupUid
+     * @param int|null $adminUid
+     * @throws NoSuchWebsiteException
+     */
+    protected function setBeUserPerms(int $organizationId, int $editorsGroupUid = null, int $adminUid = null) {
+        $rootUid = $this->findRootUidFor($organizationId);
+
+        $organizationExtraData = $this->fetchOrganizationExtraData($organizationId);
+        $isPremium = self::IS_PRODUCT_PREMIUM[$organizationExtraData['admin']['product']];
+
+        $editorsGroupUid = $this->findEditorsBeGroupUid($rootUid);
+        $adminUid = $this->findAdminBeUserUid($rootUid);
+
+        // fetch pages and root page
+        $pages = $this->otPageRepository->getAllSitePages($rootUid);
+
+        // To understand how the rights levels are computed:
+        // @see https://ressources.opentalent.fr/display/EX/Droits+des+BE+Users
+        foreach ($pages as $page) {
+
+            if ($page['is_siteroot']) {
+
+                $adminPerms = self::PERM_SHOW + self::PERM_EDIT_CONTENT + self::PERM_EDIT_PAGE;
+                if ($isPremium)  {
+                    $adminPerms += self::PERM_NEW;
+                }
+                $editorsPerms = self::PERM_SHOW + self::PERM_EDIT_CONTENT;
+
+            } else if (
+                $page['slug'] == '/footer' ||
+                $page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->legal' ||
+                $page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->contact' ||
+                $page['slug'] == '/plan-du-site'
+            ) {
+                $adminPerms = self::PERM_SHOW;
+                if ($isPremium)  {
+                    $adminPerms += self::PERM_NEW;
+                }
+                $editorsPerms = self::PERM_SHOW;
+
+            } else if (
+                $page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->members' ||
+                $page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->membersCa' ||
+                $page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->structures' ||
+                $page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->events' ||
+                $page['tx_fed_page_controller_action'] == 'OpenTalent.OtTemplating->structuresEvents'
+            ) {
+                $adminPerms = self::PERM_SHOW;
+                if ($isPremium)  {
+                    $adminPerms += self::PERM_NEW + self::PERM_EDIT_PAGE;
+                }
+                $editorsPerms = self::PERM_SHOW;
+
+            } else {
+                $adminPerms = self::PERM_SHOW + self::PERM_EDIT_CONTENT + self::PERM_EDIT_PAGE + self::PERM_NEW;
+                if ($isPremium)  {
+                    $adminPerms += self::PERM_DELETE;
+                }
+
+                $editorsPerms = self::PERM_SHOW + self::PERM_EDIT_CONTENT + self::PERM_EDIT_PAGE;
+                if ($isPremium)  {
+                    $editorsPerms += self::PERM_NEW;
+                }
+            }
+
+            $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
+            $queryBuilder
+                ->update('pages')
+                ->where($queryBuilder->expr()->eq('uid', $page['uid']))
+                ->set('perms_userid', $adminUid)
+                ->set('perms_groupid', $editorsGroupUid)
+                ->set('perms_user', $adminPerms)
+                ->set('perms_group', $editorsPerms)
+                ->set('perms_everybody', 0)
+                ->execute();
+        }
+
+        return $rootUid;
+    }
+
+    /**
+     * CLI action for resetting the rights of admin and editors of the website
+     * on all of the existing pages, including deleted ones
+     *
+     * @param $organizationId
+     * @throws NoSuchWebsiteException
+     * @throws \Doctrine\DBAL\ConnectionException
+     * @throws \Doctrine\DBAL\DBALException
+     * @throws \Throwable
+     */
+    public function resetBeUserPermsAction($organizationId) {
+        $this->connectionPool->getConnectionByName('Default')->beginTransaction();
+        try {
+            $rootUid = $this->setBeUserPerms($organizationId);
+
+            $commitSuccess = $this->connectionPool->getConnectionByName('Default')->commit();
+            if (!$commitSuccess) {
+                throw new \RuntimeException('Something went wrong while commiting the result');
+            }
+            return $rootUid;
+        }
+        catch (\Throwable $e) {
+            // rollback
+            $this->connectionPool->getConnectionByName('Default')->rollback();
+            throw $e;
+        }
+    }
+
+    /**
+     * CLI action for resetting the rights of admin and editors of every website
+     * on all of the existing pages, including deleted ones
+     *
+     * @throws NoSuchWebsiteException
+     * @throws \Doctrine\DBAL\ConnectionException
+     * @throws \Doctrine\DBAL\DBALException
+     * @throws \Throwable
+     */
+    public function resetAllBeUserPermsAction() {
+        $this->connectionPool->getConnectionByName('Default')->beginTransaction();
+        try {
+            $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
+            $queryBuilder->getRestrictions()->removeAll();
+            $sites = $queryBuilder
+                ->select('tx_opentalent_structure_id')
+                ->from('pages')
+                ->where('is_siteroot=1')
+                ->andWhere($queryBuilder->expr()->gt('tx_opentalent_structure_id', 0))
+                ->execute()
+                ->fetchAll();
+
+            foreach ($sites as $site) {
+                $this->setBeUserPerms($site['tx_opentalent_structure_id']);
+            }
+
+            $commitSuccess = $this->connectionPool->getConnectionByName('Default')->commit();
+            if (!$commitSuccess) {
+                throw new \RuntimeException('Something went wrong while committing the result');
+            }
+        }
+        catch (\Throwable $e) {
+            // rollback
+            $this->connectionPool->getConnectionByName('Default')->rollback();
+            throw $e;
+        }
+    }
+
     /**
      * Retrieve the Organization object from the repository and then,
      * from the Opentalent API
@@ -1667,7 +1869,10 @@ class SiteController extends ActionController
     {
 
         $configs_directory = $_ENV['TYPO3_PATH_APP'] . "/config/sites/";
-        $candidates = scandir($configs_directory);
+        $candidates = array_filter(
+                scandir($configs_directory),
+                function ($x) { return $x != '.' && $x != '..'; }
+            );
 
         // try to filter by directory name
         foreach ($candidates as $subdir) {
@@ -1726,8 +1931,8 @@ class SiteController extends ActionController
      * @param bool $forceRecreate
      * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
      */
-    private function writeConfigFile(int $organizationId, int $rootUid, string $domain, bool $forceRecreate = false) {
-
+    private function writeConfigFile(int $organizationId, int $rootUid, string $domain, bool $forceRecreate = false): string
+    {
         $existing = $this->findConfigFileAndContentFor($organizationId, $rootUid);
         $configFilename = $existing[0];
         $config = $existing[1];
@@ -1787,7 +1992,7 @@ class SiteController extends ActionController
         if (!file_exists($configDir)) {
             $this->mkDir($configDir);
         }
-        GeneralUtility::writeFile($configFilename, $yamlConfig);
+        $this->writeFile($configFilename, $yamlConfig);
 
         // Set the owner and mods, in case www-data is not the one who run this command
         // @see https://www.php.net/manual/fr/function.stat.php
@@ -1803,6 +2008,8 @@ class SiteController extends ActionController
         $cacheSystem = $this->cacheManager->getCache('cache_core');
         $cacheSystem->remove('site-configuration');
         $cacheSystem->remove('pseudo-sites');
+
+        return $configFilename;
     }
 
     /**
@@ -1811,12 +2018,14 @@ class SiteController extends ActionController
      *
      * @param int $organizationId
      * @param int $rootUid
+     * @param int $siteGroupUid
      * @param string $domain
      * @param array $userData
      * @return int The uid of the created be_user
      */
     private function createBeUser(int $organizationId,
                                   int $rootUid,
+                                  int $siteGroupUid,
                                   string $domain,
                                   array $userData): int
     {
@@ -1828,6 +2037,49 @@ class SiteController extends ActionController
         // Since we don't want to store the password in the TYPO3 DB, we store a random string instead
         $randomStr = (new Random)->generateRandomHexString(20);
 
+        $adminGroup = self::IS_PRODUCT_PREMIUM[$userData['product']] ? self::BEGROUP_ADMIN_PREMIUM : self::BEGROUP_ADMIN_STANDARD;
+        $adminGroupUid = $this->getBaseBeGroupUid($adminGroup);
+
+        $values = [
+            'username' => $userData['username'],
+            'password' => $randomStr,
+            'description' => '[ATTENTION: enregistrement auto-généré, ne pas modifier directement] BE Admin for ' . $domain . ' (id: ' . $userData['id'] . ')',
+            'deleted' => 0,
+            'lang' => 'fr',
+            'usergroup' => $siteGroupUid . ',' . $adminGroupUid,
+            'userMods' => 'file_FilelistList',
+            'db_mountpoints' => $rootUid,
+            'options' => 2,
+            'tx_opentalent_opentalentId' => $userData['id'],
+            'tx_opentalent_organizationId' => $organizationId,
+            'tx_opentalent_generationDate' => date('Y/m/d H:i:s')
+        ];
+
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_users');
+        $queryBuilder->insert('be_users')
+            ->values($values)
+            ->execute();
+
+        return $queryBuilder->getConnection()->lastInsertId();
+    }
+
+    /**
+     * Create the BE editors group for the website
+     *
+     * @param int $organizationId
+     * @param int $rootUid
+     * @param string $domain
+     * @param array $userData
+     * @return int The uid of the created be_user
+     */
+    private function createBeGroup(int $organizationId,
+                                  int $rootUid,
+                                  string $domain,
+                                  array $userData): int
+    {
+
+        $groupName = 'edit_' . explode('.', $domain)[0];
+
         // get the existing filemounts
         $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_filemounts');
         $queryBuilder
@@ -1841,31 +2093,137 @@ class SiteController extends ActionController
             $files[] = $row[0];
         }
 
+        $mainGroup = self::IS_PRODUCT_PREMIUM[$userData['product']] ? self::BEGROUP_EDITOR_PREMIUM : self::BEGROUP_EDITOR_STANDARD;
+        $mainGroupUid = $this->getBaseBeGroupUid($mainGroup);
+
         $values = [
-            'username' => $userData['username'],
-            'password' => $randomStr,
-            'description' => '[ATTENTION: enregistrement auto-généré, ne pas modifier directement] BE Admin for ' . $domain . ' (id: ' . $userData['id'] . ')',
+            'title' => $groupName,
             'deleted' => 0,
-            'lang' => 'fr',
-            'usergroup' => isset(self::PRODUCT_MAPPING[$user_data['product']]) ? self::PRODUCT_MAPPING[$userData['product']] : 1,
+            'subgroup' => $mainGroupUid,
             'db_mountpoints' => $rootUid,
-            'userMods' => 'file_FilelistList',
             'file_mountPoints' => join(',', $files),
-            'options' => 2,
             'file_permissions' => 'readFolder,writeFolder,addFolder,renameFolder,moveFolder,deleteFolder,readFile,writeFile,addFile,renameFile,replaceFile,moveFile,copyFile,deleteFile',
-            'tx_opentalent_opentalentId' => $userData['id'],
-            'tx_opentalent_organizationId' => $organizationId,
-            'tx_opentalent_generationDate' => date('Y/m/d H:i:s')
         ];
 
-        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_users');
-        $queryBuilder->insert('be_users')
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_groups');
+        $queryBuilder->insert('be_groups')
             ->values($values)
             ->execute();
 
         return $queryBuilder->getConnection()->lastInsertId();
     }
 
+    /**
+     * Try to find and return the uid of the editors be_group
+     * for this website
+     *
+     * @param int $rootUid
+     * @return int
+     */
+    protected function findEditorsBeGroupUid(int $rootUid): int {
+        $editorsGroups = [
+            $this->getBaseBeGroupUid(self::BEGROUP_EDITOR_STANDARD),
+            $this->getBaseBeGroupUid(self::BEGROUP_EDITOR_PREMIUM)
+        ];
+
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_groups');
+        $queryBuilder->getRestrictions()->removeAll();
+        $beGroups = $queryBuilder
+            ->select('uid', 'subgroup')
+            ->from('be_groups')
+            ->where('FIND_IN_SET(' . $rootUid . ', db_mountpoints) > 0')
+            ->execute()
+            ->fetchAll();
+
+        $editorsGroupUid = null;
+        foreach ($beGroups as $beGroup) {
+            // the editor group shall be a subgroup of one of the Editors group, and have this website mounted
+            if (in_array($beGroup['subgroup'], $editorsGroups)) {
+                $editorsGroupUid = $beGroup['uid'];
+            }
+        }
+        if ($editorsGroupUid == null) {
+            throw new \RuntimeException("No editors be_group found " .
+                "among the groups that have this website mounted (root uid: " . $rootUid . ")");
+        }
+
+        return $editorsGroupUid;
+    }
+
+    /**
+     * Try to find and return the uid of the admin be_user
+     * for this website
+     *
+     * @param int $rootUid
+     * @return int
+     */
+    protected function findAdminBeUserUid(int $rootUid): int {
+        $adminGroups = [
+            $this->getBaseBeGroupUid(self::BEGROUP_ADMIN_STANDARD),
+            $this->getBaseBeGroupUid(self::BEGROUP_ADMIN_PREMIUM)
+        ];
+
+        $editorsGroupUid = $this->findEditorsBeGroupUid($rootUid);
+
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_users');
+        $queryBuilder->getRestrictions()->removeAll();
+        $beUsers = $queryBuilder
+            ->select('uid', 'usergroup')
+            ->from('be_users')
+            ->where('FIND_IN_SET(' . $rootUid . ', db_mountpoints) > 0')
+            ->execute()
+            ->fetchAll();
+
+        $adminUid = null;
+        foreach ($beUsers as $beUser) {
+            // the admin shall be both in the website editors group and in the Admin group
+            $isAdmin = false;
+            $isInThisWebsiteGroup = false;
+            foreach (explode(',', $beUser['usergroup']) as $group) {
+                if (in_array($group, $adminGroups)) {
+                    $isAdmin = true;
+                }
+                if ($group == $editorsGroupUid) {
+                    $isInThisWebsiteGroup = true;
+                }
+            }
+
+            if ($isAdmin && $isInThisWebsiteGroup) {
+                $adminUid = $beUser['uid'];
+            }
+        }
+        if ($adminUid == null) {
+            throw new \RuntimeException("No admin be_user found " .
+                "among the users that have this website mounted (root uid: " . $rootUid . ")");
+        }
+
+        return $adminUid;
+    }
+
+    /**
+     * Return the uid of one of the base groups (BEGROUP_EDITOR_STANDARD,  BEGROUP_EDITOR_PREMIUM, ...)
+     *
+     * @param int $groupType
+     * @return int
+     */
+    protected function getBaseBeGroupUid(int $groupType): int
+    {
+        $expectedName = self::BEGROUP_NAME[$groupType];
+
+        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('be_groups');
+        $uid = $queryBuilder
+            ->select('uid')
+            ->from('be_groups')
+            ->where($queryBuilder->expr()->eq('title', $queryBuilder->expr()->literal($expectedName)))
+            ->execute()
+            ->fetchColumn(0);
+
+        if (!$uid) {
+            throw new \RuntimeException("Expects a BE group named '" . $expectedName . "', but none was found.");
+        }
+        return $uid;
+    }
+
     /**
      * Enable frontend editing for user
      *

+ 96 - 2
ot_admin/Classes/Domain/Entity/SiteInfos.php

@@ -13,9 +13,13 @@ class SiteInfos
     protected string $template = "";
     protected string $preferences = "";
     protected ?int $matomoId = null;
+    protected ?bool $isPremium = null;
     protected bool $deleted = false;
     protected bool $hiddenOrRestricted = false;
     protected array $mountedForBeUsers = [];
+    protected array $mountedForBeGroups = [];
+    protected ?int $ownerUserUid = null;
+    protected ?int $ownerGroupUid = null;
 
     /**
      * SiteInfos constructor.
@@ -28,6 +32,9 @@ class SiteInfos
      * @param bool $deleted
      * @param bool $hiddenOrRestricted
      * @param array|null $mountedForBeUsers
+     * @param array|null $mountedForBeGroups
+     * @param int|null $ownerUserUid
+     * @param int|null $ownerGroupUid
      */
     public function __construct(
         int $rootUid,
@@ -36,9 +43,13 @@ class SiteInfos
         string $template = null,
         string $preferences = null,
         int $matomoId = null,
+        bool $isPremium = null,
         bool $deleted = false,
         bool $hiddenOrRestricted = false,
-        array $mountedForBeUsers = null
+        array $mountedForBeUsers = null,
+        array $mountedForBeGroups = null,
+        int $ownerUserUid = null,
+        int $ownerGroupUid = null
     )
     {
         $this->rootUid = $rootUid;
@@ -57,6 +68,9 @@ class SiteInfos
         if ($matomoId !== null) {
             $this->matomoId = $matomoId;
         }
+        if ($isPremium !== null) {
+            $this->isPremium = $isPremium;
+        }
         if ($deleted !== null) {
             $this->deleted = $deleted;
         }
@@ -66,6 +80,15 @@ class SiteInfos
         if ($mountedForBeUsers !== null) {
             $this->mountedForBeUsers = $mountedForBeUsers;
         }
+        if ($mountedForBeGroups !== null) {
+            $this->mountedForBeGroups = $mountedForBeGroups;
+        }
+        if ($ownerUserUid !== null) {
+            $this->ownerUserUid = $ownerUserUid;
+        }
+        if ($ownerGroupUid !== null) {
+            $this->ownerGroupUid = $ownerGroupUid;
+        }
     }
 
     /**
@@ -164,6 +187,22 @@ class SiteInfos
         $this->matomoId = $matomoId;
     }
 
+    /**
+     * @return bool | null
+     */
+    public function isPremium(): ?bool
+    {
+        return $this->isPremium;
+    }
+
+    /**
+     * @param bool $isPremium
+     */
+    public function setIsPremium(bool $isPremium): void
+    {
+        $this->isPremium = $isPremium;
+    }
+
     /**
      * @return bool
      */
@@ -213,11 +252,66 @@ class SiteInfos
     }
 
     /**
-     * @param array $beUser
+     * @param array $beUser ['uid' => int, 'username' => string]
      */
     public function addMountedForBeUser(array $beUser): void
     {
         $this->mountedForBeUsers[] = $beUser;
     }
 
+    /**
+     * @return array
+     */
+    public function getMountedForBeGroups(): array
+    {
+        return $this->mountedForBeGroups;
+    }
+
+    /**
+     * @param array $mountedForBeGroups
+     */
+    public function setMountedForBeGroups(array $mountedForBeGroups): void
+    {
+        $this->mountedForBeGroups = $mountedForBeGroups;
+    }
+
+    /**
+     * @param array $beGroup
+     */
+    public function addMountedForBeGroups(array $beGroup): void
+    {
+        $this->mountedForBeGroups[] = $beGroup;
+    }
+
+    /**
+     * @return int|null
+     */
+    public function getOwnerUserUid(): ?int
+    {
+        return $this->ownerUserUid;
+    }
+
+    /**
+     * @param int|null $ownerUserUid
+     */
+    public function setOwnerUserUid(?int $ownerUserUid): void
+    {
+        $this->ownerUserUid = $ownerUserUid;
+    }
+
+    /**
+     * @return int|null
+     */
+    public function getOwnerGroupUid(): ?int
+    {
+        return $this->ownerGroupUid;
+    }
+
+    /**
+     * @param int|null $ownerGroupUid
+     */
+    public function setOwnerGroupUid(?int $ownerGroupUid): void
+    {
+        $this->ownerGroupUid = $ownerGroupUid;
+    }
 }

+ 6 - 2
ot_admin/Classes/Domain/Entity/SiteStatus.php

@@ -86,10 +86,14 @@ class SiteStatus
             $infos["Root Uid"] = $this->getSiteInfos()->getRootUid();
             $infos["Site's title"] = $this->getSiteInfos()->getSiteTitle();
             $infos["Base Url"] = $this->getSiteInfos()->getBaseUrl();
+            $infos["Deleted"] = (int)$this->getSiteInfos()->isDeleted();
+            $infos["Hidden or restricted"] = (int)$this->getSiteInfos()->isHiddenOrRestricted();
+            $infos["Premium"] = (int)$this->getSiteInfos()->isPremium();
             $infos["Template"] = $this->getSiteInfos()->getTemplate();
             $infos["Preferences"] = $this->getSiteInfos()->getPreferences();
-            $infos["Matomo id"] = $this->getSiteInfos()->getMatomoId();
-            $infos["Mounted for: "] = $this->getSiteInfos()->getMountedForBeUsers();
+            $infos["Matomo id"] = $this->getSiteInfos()->getMatomoId() ?? "-";
+            $infos["Mounted for user(s): "] = $this->getSiteInfos()->getMountedForBeUsers();
+            $infos["Mounted for group(s): "] = $this->getSiteInfos()->getMountedForBeGroups();
 
             if ($this->useWarnings()) {
                 $infos["Warnings"] = $this->getWarnings();

+ 3 - 0
ot_admin/Configuration/Commands.php

@@ -28,6 +28,9 @@ return [
     'ot:site:setdomain' => [
         'class' => Opentalent\OtAdmin\Command\SetSiteDomainCommand::class
     ],
+    'ot:site:reset-perms' => [
+        'class' => Opentalent\OtAdmin\Command\ResetBeUserPermsCommand::class
+    ],
     'ot:site:status' => [
         'class' => Opentalent\OtAdmin\Command\GetSiteStatusCommand::class
     ],

+ 6 - 0
ot_admin/Configuration/Services.yaml

@@ -37,6 +37,12 @@ services:
         command: 'ot:site:setdomain'
         schedulable: true
 
+  Opentalent\OtAdmin\Command\ResetBeUserPermsCommand:
+    tags:
+      - name: 'ot:site:reset-perms'
+        command: 'ot:site:reset-perms'
+        schedulable: true
+
   Opentalent\OtAdmin\Command\GetSiteStatusCommand:
     tags:
       - name: 'ot:site:status'