Skip to content

Commit

Permalink
Merge pull request #8 from xima-media/user-groups
Browse files Browse the repository at this point in the history
Remote user group mapping, profile images for backend users
  • Loading branch information
maikschneider authored Jan 7, 2024
2 parents ccad9b0 + aecc9db commit b19f1dd
Show file tree
Hide file tree
Showing 19 changed files with 624 additions and 323 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
core
public/_assets
public/index.php
public/fileadmin
public/typo3
public/typo3conf

vendor
vendor
var
11 changes: 6 additions & 5 deletions Classes/EventListener/BackendUserLookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Waldhacker\Oauth2Client\Events\BackendUserLookupEvent;
use Xima\XimaOauth2Extended\Exception\IdentityResolverException;
use Xima\XimaOauth2Extended\ResourceResolver\ResolverOptions;
use Xima\XimaOauth2Extended\ResourceResolver\ResourceResolverInterface;
use Xima\XimaOauth2Extended\UserFactory\BackendUserFactory;

Expand All @@ -34,20 +35,20 @@ public function __invoke(BackendUserLookupEvent $event): void

$providerId = $event->getProviderId();
$extendedProviderConfiguration = $this->extensionConfiguration->get('xima_oauth2_extended', 'oauth2_client_providers') ?? [];
$resolverClass = $extendedProviderConfiguration[$providerId]['resolverClassName'] ?? '';
if (!$resolverClass) {
$resolverOptions = ResolverOptions::createFromExtensionConfiguration($extendedProviderConfiguration[$providerId] ?? []);
if (!$resolverOptions->resolverClassName) {
return;
}

// create resolver
$resolver = GeneralUtility::makeInstance($resolverClass, $event);
$resolver = GeneralUtility::makeInstance($resolverOptions->resolverClassName, $event, $resolverOptions);
if (!$resolver instanceof ResourceResolverInterface) {
$message = 'Class ' . $resolverClass . ' musst implement interface ' . ResourceResolverInterface::class;
$message = 'Class ' . $resolverOptions->resolverClassName . ' musst implement interface ' . ResourceResolverInterface::class;
throw new IdentityResolverException($message, 1683016777);
}

// create/link user or update
$userFactory = new BackendUserFactory($resolver, $providerId, $extendedProviderConfiguration);
$userFactory = new BackendUserFactory($resolver, $providerId);
$typo3User = $event->getTypo3User();
if ($typo3User === null) {
$this->logger->info('Register remote user from provider "' . $event->getProviderId() . '" (remote id: ' . $event->getRemoteUser()->getId() . ')');
Expand Down
9 changes: 5 additions & 4 deletions Classes/EventListener/FrontendUserLookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Waldhacker\Oauth2Client\Events\FrontendUserLookupEvent;
use Xima\XimaOauth2Extended\Exception\IdentityResolverException;
use Xima\XimaOauth2Extended\Exception\OAuth2ConfigurationException;
use Xima\XimaOauth2Extended\ResourceResolver\ResolverOptions;
use Xima\XimaOauth2Extended\ResourceResolver\ResourceResolverInterface;
use Xima\XimaOauth2Extended\UserFactory\FrontendUserFactory;

Expand All @@ -39,15 +40,15 @@ public function __invoke(FrontendUserLookupEvent $event): void

$providerId = $event->getProviderId();
$extendedProviderConfiguration = $this->extensionConfiguration->get('xima_oauth2_extended', 'oauth2_client_providers') ?? [];
$resolverClass = $extendedProviderConfiguration[$providerId]['resolverClassName'] ?? '';
if (!$resolverClass) {
$resolverOptions = ResolverOptions::createFromExtensionConfiguration($extendedProviderConfiguration[$providerId] ?? []);
if (!$resolverOptions->resolverClassName) {
return;
}

// create resolver
$resolver = GeneralUtility::makeInstance($resolverClass, $event);
$resolver = GeneralUtility::makeInstance($resolverOptions->resolverClassName, $event, $resolverOptions);
if (!$resolver instanceof ResourceResolverInterface) {
$message = 'Class ' . $resolverClass . ' musst implement interface ' . ResourceResolverInterface::class;
$message = 'Class ' . $resolverOptions->resolverClassName . ' musst implement interface ' . ResourceResolverInterface::class;
throw new IdentityResolverException($message, 1683016777);
}

Expand Down
8 changes: 7 additions & 1 deletion Classes/ResourceResolver/AbstractResourceResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ public function getRemoteUser(): ResourceOwnerInterface
return $this->userLookupEvent->getRemoteUser();
}

public function getOptions(): ResolverOptions
{
return $this->options;
}

public function __construct(
protected readonly FrontendUserLookupEvent|BackendUserLookupEvent $userLookupEvent
protected readonly FrontendUserLookupEvent|BackendUserLookupEvent $userLookupEvent,
protected readonly ResolverOptions $options
) {
}
}
19 changes: 10 additions & 9 deletions Classes/ResourceResolver/GenericResourceResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ class GenericResourceResolver extends AbstractResourceResolver
{
public function getIntendedEmail(): ?string
{
return $this->getRemoteUser()->toArray()['email'] ?? null;
if ($this->getRemoteUser()->toArray()['email'] ?? false) {
return strtolower($this->getRemoteUser()->toArray()['email']);
}
return null;
}

public function getIntendedUsername(): ?string
Expand All @@ -19,14 +22,14 @@ public function updateBackendUser(array &$beUser): void
$remoteUser = $this->getRemoteUser()->toArray();

if (!$beUser['username'] && $remoteUser['email']) {
$beUser['username'] = $remoteUser['email'];
$beUser['username'] = strtolower($remoteUser['email']);
}

if ($remoteUser['email']) {
$beUser['email'] = $remoteUser['email'];
$beUser['email'] = strtolower($remoteUser['email']);
}

$beUser['disable'] = 0;
$beUser['disable'] = 0;

if (!$beUser['realName']) {
$beUser['realName'] = $remoteUser['name'];
Expand All @@ -38,19 +41,17 @@ public function updateFrontendUser(array &$feUser): void
$remoteUser = $this->getRemoteUser()->toArray();

if (!$feUser['username'] && $remoteUser['email']) {
$feUser['username'] = $remoteUser['email'];
$feUser['username'] = strtolower($remoteUser['email']);
}

if ($remoteUser['email']) {
$feUser['email'] = $remoteUser['email'];
$feUser['email'] = strtolower($remoteUser['email']);
}

$feUser['disable'] = 0;
$feUser['disable'] = 0;

if (!$feUser['name']) {
$feUser['name'] = $remoteUser['name'];
}

// @TODO: usergroups
}
}
4 changes: 2 additions & 2 deletions Classes/ResourceResolver/GitlabResourceResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function updateBackendUser(array &$beUser): void
}

if ($remoteUser['email']) {
$beUser['email'] = $remoteUser['email'];
$beUser['email'] = strtolower($remoteUser['email']);
}

if ($remoteUser['state'] === 'active') {
Expand All @@ -38,7 +38,7 @@ public function updateFrontendUser(array &$feUser): void
}

if ($remoteUser['email']) {
$feUser['email'] = $remoteUser['email'];
$feUser['email'] = strtolower($remoteUser['email']);
}

if ($remoteUser['state'] === 'active') {
Expand Down
37 changes: 31 additions & 6 deletions Classes/ResourceResolver/MicrosoftResourceResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,21 @@
use TYPO3\CMS\Core\Http\RequestFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class MicrosoftResourceResolver extends GenericResourceResolver implements ProfileImageResolverInterface
class MicrosoftResourceResolver extends GenericResourceResolver implements ProfileImageResolverInterface, UserGroupResolverInterface
{
public function updateBackendUser(array &$beUser): void
{
$remoteUser = $this->getRemoteUser()->toArray();

if (!$beUser['username'] && $remoteUser['email']) {
$beUser['username'] = $remoteUser['email'];
$beUser['username'] = strtolower($remoteUser['email']);
}

if ($remoteUser['email']) {
$beUser['email'] = $remoteUser['email'];
$beUser['email'] = strtolower($remoteUser['email']);
}

$beUser['disable'] = 0;
$beUser['admin'] = 1;

if (!$beUser['realName']) {
$beUser['realName'] = $remoteUser['name'];
Expand Down Expand Up @@ -57,11 +56,11 @@ public function updateFrontendUser(array &$feUser): void
$remoteUser = $this->getRemoteUser()->toArray();

if (!$feUser['username'] && $remoteUser['email']) {
$feUser['username'] = $remoteUser['email'];
$feUser['username'] = strtolower($remoteUser['email']);
}

if ($remoteUser['email']) {
$feUser['email'] = $remoteUser['email'];
$feUser['email'] = strtolower($remoteUser['email']);
}

$feUser['disable'] = 0;
Expand All @@ -70,4 +69,30 @@ public function updateFrontendUser(array &$feUser): void
$feUser['name'] = $remoteUser['name'];
}
}

public function resolveUserGroups(): array
{
$ownerUrl = 'https://graph.microsoft.com/v1.0/me/memberOf';

$requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
try {
$groupResource = $requestFactory->request(
$ownerUrl,
'GET',
[
'headers' => [
'Authorization' => 'Bearer ' . $this->userLookupEvent->getAccessToken(),
],
]
);
$body = $groupResource->getBody()->getContents();
$groupSettings = json_decode($body);

return array_map(function ($group) {
return $group->id;
}, $groupSettings?->value ?? []);
} catch (\Exception) {
}
return [];
}
}
56 changes: 56 additions & 0 deletions Classes/ResourceResolver/ResolverOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Xima\XimaOauth2Extended\ResourceResolver;

final class ResolverOptions
{
/** @var class-string */
public string $resolverClassName;

public bool $createBackendUser = false;

public bool $createFrontendUser = false;

public string $defaultBackendUsergroup = '';

public string $defaultFrontendUsergroup = '';

public bool $createBackendUsergroups = false;

public bool $createFrontendUsergroups = false;

public bool $requireBackendUsergroup = false;

public bool $requireFrontendUsergroup = false;

public string $imageStorageBackendIdentifier = '';

public string $imageStorageFrontendIdentifier = '';

public string $defaultBackendLanguage = '';

public string $defaultBackendAdminGroups = '';

/**
* @param array<string, mixed> $extConf
*/
public static function createFromExtensionConfiguration(array $extConf): self
{
$conf = new self();
$conf->resolverClassName = $extConf['resolverClassName'] ?? '';
$conf->createBackendUser = $extConf['createBackendUser'] ?? false;
$conf->createFrontendUser = $extConf['createFrontendUser'] ?? false;
$conf->defaultBackendUsergroup = $extConf['defaultBackendUsergroup'] ?? '';
$conf->defaultFrontendUsergroup = $extConf['defaultFrontendUsergroup'] ?? '';
$conf->createBackendUsergroups = $extConf['createBackendUsergroups'] ?? false;
$conf->createFrontendUsergroups = $extConf['createFrontendUsergroups'] ?? false;
$conf->requireBackendUsergroup = $extConf['requireBackendUsergroup'] ?? false;
$conf->requireFrontendUsergroup = $extConf['requireFrontendUsergroup'] ?? false;
$conf->imageStorageBackendIdentifier = $extConf['imageStorageBackendIdentifier'] ?? '1:/user_upload/oauth';
$conf->imageStorageFrontendIdentifier = $extConf['imageStorageFrontendIdentifier'] ?? '1:/user_upload/oauth';
$conf->defaultBackendLanguage = $extConf['defaultBackendLanguage'] ?? 'default';
$conf->defaultBackendAdminGroups = $extConf['defaultBackendAdminGroups'] ?? '';

return $conf;
}
}
2 changes: 2 additions & 0 deletions Classes/ResourceResolver/ResourceResolverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ public function getIntendedUsername(): ?string;
public function getIntendedEmail(): ?string;

public function getRemoteUser(): ResourceOwnerInterface;

public function getOptions(): ResolverOptions;
}
13 changes: 13 additions & 0 deletions Classes/ResourceResolver/UserGroupResolverInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Xima\XimaOauth2Extended\ResourceResolver;

interface UserGroupResolverInterface extends ResourceResolverInterface
{
/**
* Returns list of `oauth2_id`s
*
* @return string[]
*/
public function resolveUserGroups(): array;
}
30 changes: 30 additions & 0 deletions Classes/UserFactory/AbstractUserFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Xima\XimaOauth2Extended\UserFactory;

use Xima\XimaOauth2Extended\ResourceResolver\ResourceResolverInterface;
use Xima\XimaOauth2Extended\ResourceResolver\UserGroupResolverInterface;

abstract class AbstractUserFactory
{
/** @var string[]|null */
protected ?array $remoteGroupIds = null;

public function __construct(
protected ResourceResolverInterface $resolver,
protected string $providerId
) {
}

/** @return string[] */
public function getRemoteGroupIdsCached(): array
{
if (!$this->resolver instanceof UserGroupResolverInterface) {
return [];
}
if ($this->remoteGroupIds === null) {
$this->remoteGroupIds = $this->resolver->resolveUserGroups();
}
return $this->remoteGroupIds;
}
}
Loading

0 comments on commit b19f1dd

Please sign in to comment.