Leuchtfeuer/auth0-for-typo3

View on GitHub
Classes/Utility/Database/UpdateUtility.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

declare(strict_types=1);

/*
 * This file is part of the "Auth0" extension for TYPO3 CMS.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * Florian Wessels <f.wessels@Leuchtfeuer.com>, Leuchtfeuer Digital Marketing
 */

namespace Leuchtfeuer\Auth0\Utility\Database;

use Leuchtfeuer\Auth0\Configuration\Auth0Configuration;
use Leuchtfeuer\Auth0\Domain\Repository\UserGroup\AbstractUserGroupRepository;
use Leuchtfeuer\Auth0\Domain\Repository\UserGroup\BackendUserGroupRepository;
use Leuchtfeuer\Auth0\Domain\Repository\UserGroup\FrontendUserGroupRepository;
use Leuchtfeuer\Auth0\Domain\Repository\UserRepository;
use Leuchtfeuer\Auth0\Domain\Transfer\EmAuth0Configuration;
use Leuchtfeuer\Auth0\Utility\ParseFuncUtility;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class UpdateUtility implements LoggerAwareInterface
{
    use LoggerAwareTrait;

    protected string $tableName = '';

    protected EmAuth0Configuration $configuration;

    protected $user = [];

    protected $yamlConfiguration = [];

    public function __construct(string $tableName, array $user)
    {
        $this->tableName = $tableName;
        $this->configuration = new EmAuth0Configuration();
        $this->user = $user;
        $this->yamlConfiguration = GeneralUtility::makeInstance(Auth0Configuration::class)->load();
    }

    public function updateGroups(): void
    {
        $groupMapping = $this->getGroupMappingFromDatabase();
        $this->addDefaultGroup($groupMapping);

        if (empty($groupMapping)) {
            $this->logger->error(sprintf('Cannot update user groups: No role mapping for %s found', $this->tableName));

            return;
        }

        $shouldUpdate = false;
        $isBackendAdmin = false;
        $groupsToAssign = [];

        // Map Auth0 roles on TYPO3 user groups
        $this->mapRoles($groupMapping, $groupsToAssign, $isBackendAdmin, $shouldUpdate);

        // Update user only if necessary
        if ($shouldUpdate === true) {
            $this->logger->notice('Update user groups.');
            $this->performGroupUpdate($groupsToAssign, $isBackendAdmin);
        }
    }

    public function updateUser(bool $reactivateUser = false): void
    {
        $mappingConfiguration = $this->yamlConfiguration['properties'][$this->tableName] ?? null;

        if ($mappingConfiguration === null) {
            $this->logger->error(sprintf('Cannot update user: No mapping configuration for %s found', $this->tableName));

            return;
        }

        $this->performUserUpdate($mappingConfiguration, $reactivateUser);
    }

    protected function getGroupMappingFromDatabase(): array
    {
        $groupMapping = [];
        $userGroupRepository = $this->getUserGroupRepository();

        if ($userGroupRepository instanceof AbstractUserGroupRepository) {
            foreach ($userGroupRepository->findAll() as $userGroup) {
                if (!empty($userGroup['auth0_user_group'])) {
                    $groupMapping[$userGroup[AbstractUserGroupRepository::USER_GROUP_FIELD]] = $groupMapping[$userGroup[AbstractUserGroupRepository::USER_GROUP_FIELD]] ?? [];
                    $groupMapping[$userGroup[AbstractUserGroupRepository::USER_GROUP_FIELD]][] = $userGroup['uid'];
                }
            }
        }

        return $groupMapping;
    }

    protected function getUserGroupRepository(): ?AbstractUserGroupRepository
    {
        switch ($this->tableName) {
            case 'fe_users':
                return new FrontendUserGroupRepository();

            case 'be_users':
                return new BackendUserGroupRepository();
        }

        return null;
    }

    protected function addDefaultGroup(array &$groupMapping): void
    {
        $key = 'frontend';
        $userGroupTableName = 'fe_groups';

        if ($this->tableName === 'be_users') {
            $key = 'backend';
            $userGroupTableName = 'be_groups';
        }

        $defaultGroup = (int)($this->yamlConfiguration['roles']['default'][$key] ?? 0);
        $userGroup = BackendUtility::getRecord($userGroupTableName, $defaultGroup);

        // Use default user group only when group id is not null and record exists (and is neither deleted nor hidden)
        if ($defaultGroup !== 0 && $userGroup !== null) {
            $groupMapping['__default'] = [$defaultGroup];
        }
    }

    protected function mapRoles(array $groupMapping, array &$groupsToAssign, bool &$isBeAdmin, bool &$shouldUpdate): void
    {
        $rolesKey = $this->yamlConfiguration['roles']['key'] ?? 'roles';
        $roles = (array)$this->user['app_metadata'][$rolesKey] ?? [];

        foreach ($roles as $role) {
            if (isset($groupMapping[$role])) {
                $this->logger->notice(sprintf('Assign group "%s" to user.', $groupMapping[$role]));
                $groupsToAssign = array_merge($groupsToAssign, $groupMapping[$role]);
                $shouldUpdate = true;
            } elseif (!empty($this->yamlConfiguration['roles']['beAdmin']) && $role === $this->yamlConfiguration['roles']['beAdmin']) {
                $isBeAdmin = true;
                $shouldUpdate = true;
            } else {
                $this->logger->warning(sprintf('No mapping for Auth0 role "%s" found.', $role));
            }
        }

        // Assign default group to user if no group matches
        if ($shouldUpdate === false && isset($groupMapping['__default']) && !$isBeAdmin) {
            $groupsToAssign = array_merge($groupsToAssign, $groupMapping['__default']);
            $shouldUpdate = true;
        }
    }

    protected function performGroupUpdate(array $groupsToAssign, bool $isBeAdmin): void
    {
        $updates = [];
        $groupsToAssign = array_unique($groupsToAssign);

        // Update usergroup in database
        if (!empty($groupsToAssign)) {
            $updates['usergroup'] = implode(',', $groupsToAssign);
        }

        // Set admin flag for backend users
        if ($this->tableName === 'be_users') {
            $updates['admin'] = (int)$isBeAdmin;
        }

        if (!empty($updates)) {
            $userRepository = GeneralUtility::makeInstance(UserRepository::class, $this->tableName);
            $userRepository->updateUserByAuth0Id($updates, $this->user[$this->configuration->getUserIdentifier()]);
        }
    }

    protected function performUserUpdate(array $mappingConfiguration, bool $reactivateUser): void
    {
        $this->logger->debug(
            sprintf(
                '%s: Prepare update for Auth0 user "%s"',
                $this->tableName,
                $this->user[$this->configuration->getUserIdentifier()]
            )
        );

        $updates = [];
        $userRepository = GeneralUtility::makeInstance(UserRepository::class, $this->tableName);

        $this->mapUserData($updates, $mappingConfiguration);

        // Fixed values
        // TODO: Check - seems no to be used anymore
        if ($reactivateUser) {
            $updates['disable'] = 0;
            $updates['deleted'] = 0;
        }

        $this->addRestrictions($userRepository);
        $userRepository->updateUserByAuth0Id($updates, $this->user[$this->configuration->getUserIdentifier()]);
    }

    protected function addRestrictions(UserRepository &$userRepository): void
    {
        $reactivateDeleted = false;
        $reactivateDisabled = false;

        if ($this->tableName === 'fe_users') {
            $reactivateDeleted = $this->configuration->isReactivateDeletedFrontendUsers();
            $reactivateDisabled = $this->configuration->isReactivateDisabledFrontendUsers();
        } elseif ($this->tableName === 'be_users') {
            $reactivateDeleted = $this->configuration->isReactivateDeletedBackendUsers();
            $reactivateDisabled = $this->configuration->isReactivateDisabledBackendUsers();
        } else {
            $this->logger->notice('Undefined environment');
        }

        if ($reactivateDeleted === false) {
            $userRepository->addDeletedRestriction();
        }

        if ($reactivateDisabled === false) {
            $userRepository->addDisabledRestriction();
        }
    }

    protected function mapUserData(array &$updates, array $mappingConfiguration): void
    {
        $parseFuncUtility = GeneralUtility::makeInstance(ParseFuncUtility::class);

        foreach ($mappingConfiguration as $configurationType => $properties) {
            foreach ($properties as $property) {
                $value = $parseFuncUtility->updateWithoutParseFunc($configurationType, $property['auth0Property'], $this->user);

                if (($property['processing'] ?? 'null') !== 'null') {
                    $value = $parseFuncUtility->transformValue($property['processing'], $value);
                }

                if ($value !== ParseFuncUtility::NO_AUTH0_VALUE) {
                    $updates[$property['databaseField']] = $value;
                }
            }
        }
    }
}