parent
a0d4b02057
commit
854bbdb307
@ -0,0 +1,26 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\Controller\OAuth2; |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\ServiceHelper\AuthenticationConfigHelper; |
||||||
|
use KnpU\OAuth2ClientBundle\Client\ClientRegistry; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use Symfony\Component\Routing\Attribute\Route; |
||||||
|
|
||||||
|
class AzureProviderController extends AbstractProviderController |
||||||
|
{ |
||||||
|
#[Route('/connect/azure', name: 'chamilo.oauth2_azure_start')] |
||||||
|
public function connect( |
||||||
|
ClientRegistry $clientRegistry, |
||||||
|
AuthenticationConfigHelper $authenticationConfigHelper, |
||||||
|
): Response { |
||||||
|
return $this->getStartResponse('azure', $clientRegistry, $authenticationConfigHelper); |
||||||
|
} |
||||||
|
|
||||||
|
#[Route('/connect/azure/check', name: 'chamilo.oauth2_azure_check')] |
||||||
|
public function connectCheck(): void {} |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\Security\Authenticator\OAuth2; |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\Entity\User; |
||||||
|
use Chamilo\CoreBundle\Repository\Node\UserRepository; |
||||||
|
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper; |
||||||
|
use Chamilo\CoreBundle\ServiceHelper\AuthenticationConfigHelper; |
||||||
|
use Chamilo\CoreBundle\ServiceHelper\AzureAuthenticatorHelper; |
||||||
|
use Doctrine\ORM\EntityManagerInterface; |
||||||
|
use Doctrine\ORM\NonUniqueResultException; |
||||||
|
use KnpU\OAuth2ClientBundle\Client\ClientRegistry; |
||||||
|
use League\OAuth2\Client\Token\AccessToken; |
||||||
|
use Symfony\Component\HttpFoundation\Request; |
||||||
|
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; |
||||||
|
use Symfony\Component\Routing\RouterInterface; |
||||||
|
use TheNetworg\OAuth2\Client\Provider\Azure; |
||||||
|
|
||||||
|
class AzureAuthenticator extends AbstractAuthenticator |
||||||
|
{ |
||||||
|
protected string $providerName = 'azure'; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
ClientRegistry $clientRegistry, |
||||||
|
RouterInterface $router, |
||||||
|
UserRepository $userRepository, |
||||||
|
AuthenticationConfigHelper $authenticationConfigHelper, |
||||||
|
AccessUrlHelper $urlHelper, |
||||||
|
EntityManagerInterface $entityManager, |
||||||
|
private readonly AzureAuthenticatorHelper $azureHelper, |
||||||
|
) { |
||||||
|
parent::__construct( |
||||||
|
$clientRegistry, |
||||||
|
$router, |
||||||
|
$userRepository, |
||||||
|
$authenticationConfigHelper, |
||||||
|
$urlHelper, |
||||||
|
$entityManager |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function supports(Request $request): ?bool |
||||||
|
{ |
||||||
|
return 'chamilo.oauth2_azure_check' === $request->attributes->get('_route'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws NonUniqueResultException |
||||||
|
*/ |
||||||
|
protected function userLoader(AccessToken $accessToken): User |
||||||
|
{ |
||||||
|
/** @var Azure $provider */ |
||||||
|
$provider = $this->client->getOAuth2Provider(); |
||||||
|
|
||||||
|
$me = $provider->get('/me', $accessToken); |
||||||
|
|
||||||
|
if (empty($me['mail'])) { |
||||||
|
throw new UnauthorizedHttpException( |
||||||
|
'The mail field is empty in Azure AD and is needed to set the organisation email for this user.' |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (empty($me['mailNickname'])) { |
||||||
|
throw new UnauthorizedHttpException( |
||||||
|
'The mailNickname field is empty in Azure AD and is needed to set the unique username for this user.' |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (empty($me['objectId'])) { |
||||||
|
throw new UnauthorizedHttpException( |
||||||
|
'The id field is empty in Azure AD and is needed to set the unique Azure ID for this user.' |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$userId = $this->azureHelper->registerUser($me); |
||||||
|
|
||||||
|
return $this->userRepository->find($userId); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,198 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Chamilo\CoreBundle\ServiceHelper; |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\Entity\ExtraField; |
||||||
|
use Chamilo\CoreBundle\Entity\ExtraFieldValues; |
||||||
|
use Chamilo\CoreBundle\Entity\User; |
||||||
|
use Chamilo\CoreBundle\Repository\ExtraFieldRepository; |
||||||
|
use Chamilo\CoreBundle\Repository\ExtraFieldValuesRepository; |
||||||
|
use Chamilo\CoreBundle\Repository\Node\UserRepository; |
||||||
|
use Doctrine\ORM\EntityManagerInterface; |
||||||
|
use Doctrine\ORM\NonUniqueResultException; |
||||||
|
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; |
||||||
|
|
||||||
|
readonly class AzureAuthenticatorHelper |
||||||
|
{ |
||||||
|
public const EXTRA_FIELD_ORGANISATION_EMAIL = 'organisationemail'; |
||||||
|
public const EXTRA_FIELD_AZURE_ID = 'azure_id'; |
||||||
|
public const EXTRA_FIELD_AZURE_UID = 'azure_uid'; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
private ExtraFieldValuesRepository $extraFieldValuesRepo, |
||||||
|
private ExtraFieldRepository $extraFieldRepo, |
||||||
|
private UserRepository $userRepository, |
||||||
|
private EntityManagerInterface $entityManager, |
||||||
|
private AccessUrlHelper $urlHelper, |
||||||
|
) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws NonUniqueResultException |
||||||
|
*/ |
||||||
|
public function registerUser(array $azureUserInfo, string $azureUidKey = 'objectId'): User |
||||||
|
{ |
||||||
|
if (empty($azureUserInfo)) { |
||||||
|
throw new UnauthorizedHttpException('User info not found.'); |
||||||
|
} |
||||||
|
|
||||||
|
[ |
||||||
|
$firstNme, |
||||||
|
$lastName, |
||||||
|
$username, |
||||||
|
$email, |
||||||
|
$phone, |
||||||
|
$authSource, |
||||||
|
$active, |
||||||
|
$extra, |
||||||
|
] = $this->formatUserData($azureUserInfo, $azureUidKey); |
||||||
|
|
||||||
|
$userId = $this->getUserIdByVerificationOrder($azureUserInfo, $azureUidKey); |
||||||
|
|
||||||
|
if (empty($userId)) { |
||||||
|
$user = (new User()) |
||||||
|
->setCreatorId($this->userRepository->getRootUser()->getId()) |
||||||
|
; |
||||||
|
} else { |
||||||
|
$user = $this->userRepository->find($userId); |
||||||
|
} |
||||||
|
|
||||||
|
$user |
||||||
|
->setFirstname($firstNme) |
||||||
|
->setLastname($lastName) |
||||||
|
->setEmail($email) |
||||||
|
->setUsername($username) |
||||||
|
->setPlainPassword('azure') |
||||||
|
->setStatus(STUDENT) |
||||||
|
->setAuthSource($authSource) |
||||||
|
->setPhone($phone) |
||||||
|
->setActive($active) |
||||||
|
->setRoleFromStatus(STUDENT) |
||||||
|
; |
||||||
|
|
||||||
|
$this->userRepository->updateUser($user); |
||||||
|
|
||||||
|
$url = $this->urlHelper->getCurrent(); |
||||||
|
$url->addUser($user); |
||||||
|
|
||||||
|
$this->entityManager->flush(); |
||||||
|
|
||||||
|
$this->extraFieldValuesRepo->updateItemData( |
||||||
|
$this->getOrganizationEmailField(), |
||||||
|
$user, |
||||||
|
$extra['extra_'.self::EXTRA_FIELD_ORGANISATION_EMAIL] |
||||||
|
); |
||||||
|
|
||||||
|
$this->extraFieldValuesRepo->updateItemData( |
||||||
|
$this->getAzureIdField(), |
||||||
|
$user, |
||||||
|
$extra['extra_'.self::EXTRA_FIELD_AZURE_ID] |
||||||
|
); |
||||||
|
|
||||||
|
$this->extraFieldValuesRepo->updateItemData( |
||||||
|
$this->getAzureUidField(), |
||||||
|
$user, |
||||||
|
$extra['extra_'.self::EXTRA_FIELD_AZURE_UID] |
||||||
|
); |
||||||
|
|
||||||
|
return $user; |
||||||
|
} |
||||||
|
|
||||||
|
private function getOrganizationEmailField() |
||||||
|
{ |
||||||
|
return $this->extraFieldRepo->findByVariable( |
||||||
|
ExtraField::USER_FIELD_TYPE, |
||||||
|
self::EXTRA_FIELD_ORGANISATION_EMAIL |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
private function getAzureIdField() |
||||||
|
{ |
||||||
|
return $this->extraFieldRepo->findByVariable( |
||||||
|
ExtraField::USER_FIELD_TYPE, |
||||||
|
self::EXTRA_FIELD_AZURE_ID |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
private function getAzureUidField() |
||||||
|
{ |
||||||
|
return $this->extraFieldRepo->findByVariable( |
||||||
|
ExtraField::USER_FIELD_TYPE, |
||||||
|
self::EXTRA_FIELD_AZURE_UID |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws NonUniqueResultException |
||||||
|
*/ |
||||||
|
public function getUserIdByVerificationOrder(array $azureUserData, string $azureUidKey = 'objectId'): ?int |
||||||
|
{ |
||||||
|
$selectedOrder = $this->getExistingUserVerificationOrder(); |
||||||
|
|
||||||
|
$organisationEmailField = $this->getOrganizationEmailField(); |
||||||
|
$azureIdField = $this->getAzureIdField(); |
||||||
|
$azureUidField = $this->getAzureUidField(); |
||||||
|
|
||||||
|
/** @var array<int, ExtraFieldValues> $positionsAndFields */ |
||||||
|
$positionsAndFields = [ |
||||||
|
1 => $this->extraFieldValuesRepo->findByVariableAndValue($organisationEmailField, $azureUserData['mail']), |
||||||
|
2 => $this->extraFieldValuesRepo->findByVariableAndValue($azureIdField, $azureUserData['mailNickname']), |
||||||
|
3 => $this->extraFieldValuesRepo->findByVariableAndValue($azureUidField, $azureUserData[$azureUidKey]), |
||||||
|
]; |
||||||
|
|
||||||
|
foreach ($selectedOrder as $position) { |
||||||
|
if (!empty($positionsAndFields[$position])) { |
||||||
|
return $positionsAndFields[$position]->getItemId(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public function getExistingUserVerificationOrder(): array |
||||||
|
{ |
||||||
|
return [1, 2, 3]; |
||||||
|
} |
||||||
|
|
||||||
|
private function formatUserData( |
||||||
|
array $azureUserData, |
||||||
|
string $azureUidKey |
||||||
|
): array { |
||||||
|
$phone = null; |
||||||
|
|
||||||
|
if (isset($azureUserData['telephoneNumber'])) { |
||||||
|
$phone = $azureUserData['telephoneNumber']; |
||||||
|
} elseif (isset($azureUserData['businessPhones'][0])) { |
||||||
|
$phone = $azureUserData['businessPhones'][0]; |
||||||
|
} elseif (isset($azureUserData['mobilePhone'])) { |
||||||
|
$phone = $azureUserData['mobilePhone']; |
||||||
|
} |
||||||
|
|
||||||
|
// If the option is set to create users, create it |
||||||
|
$firstNme = $azureUserData['givenName']; |
||||||
|
$lastName = $azureUserData['surname']; |
||||||
|
$email = $azureUserData['mail']; |
||||||
|
$username = $azureUserData['userPrincipalName']; |
||||||
|
$authSource = 'azure'; |
||||||
|
$active = ($azureUserData['accountEnabled'] ? 1 : 0); |
||||||
|
$extra = [ |
||||||
|
'extra_'.self::EXTRA_FIELD_ORGANISATION_EMAIL => $azureUserData['mail'], |
||||||
|
'extra_'.self::EXTRA_FIELD_AZURE_ID => $azureUserData['mailNickname'], |
||||||
|
'extra_'.self::EXTRA_FIELD_AZURE_UID => $azureUserData[$azureUidKey], |
||||||
|
]; |
||||||
|
|
||||||
|
return [ |
||||||
|
$firstNme, |
||||||
|
$lastName, |
||||||
|
$username, |
||||||
|
$email, |
||||||
|
$phone, |
||||||
|
$authSource, |
||||||
|
$active, |
||||||
|
$extra, |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue