custom/static-plugins/WabsAuth/src/Helper/AuthHelper.php line 472

Open in your IDE?
  1. <?php
  2. namespace WabsAuth\Helper;
  3. use DateTimeImmutable;
  4. use Exception;
  5. use Lcobucci\JWT\Signer\Rsa\Sha256;
  6. use Shopware\Core\Checkout\Customer\Aggregate\CustomerGroup\CustomerGroupEntity;
  7. use Shopware\Core\Checkout\Customer\CustomerEntity;
  8. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  11. use Shopware\Core\Framework\Uuid\Uuid;
  12. use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;
  13. use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
  14. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  15. use Shopware\Core\System\SystemConfig\SystemConfigService;
  16. use Symfony\Component\HttpFoundation\Session\Session;
  17. use WabsAuth\Helper\Keycloak\KeycloakProvider;
  18. class AuthHelper
  19. {
  20.     private SystemConfigService $systemConfigService;
  21.     private SalesChannelRepositoryInterface $countryRepository;
  22.     private SalesChannelRepositoryInterface $salutationRepository;
  23.     private NumberRangeValueGeneratorInterface $numberRangeValueGenerator;
  24.     private EntityRepositoryInterface $customerRepository;
  25.     private EntityRepositoryInterface $customerGroupRepository;
  26.     private EntityRepositoryInterface $wabsAuthCustomerRepository;
  27.     private array $groupMapping;
  28.     private bool $noAdvancedMode;
  29.     private Session $session;
  30.     private AddressMatchingHelper $addressMatchingHelper;
  31.     private DebugHelper $logger;
  32.     public bool $organizationAddressFlag false;
  33.     /**
  34.      * AuthHelper constructor.
  35.      * @param SystemConfigService $systemConfigService
  36.      * @param SalesChannelRepositoryInterface $countryRepository
  37.      * @param SalesChannelRepositoryInterface $salutationRepository
  38.      * @param NumberRangeValueGeneratorInterface $numberRangeValueGenerator
  39.      * @param EntityRepositoryInterface $customerRepository
  40.      * @param EntityRepositoryInterface $wabsAuthCustomerRepository
  41.      * @param EntityRepositoryInterface $customerGroupRepository
  42.      * @param AddressMatchingHelper $addressMatchingHelper
  43.      * @param Session $session
  44.      * @param DebugHelper $logger
  45.      */
  46.     public function __construct(
  47.         SystemConfigService $systemConfigService,
  48.         SalesChannelRepositoryInterface $countryRepository,
  49.         SalesChannelRepositoryInterface $salutationRepository,
  50.         NumberRangeValueGeneratorInterface $numberRangeValueGenerator,
  51.         EntityRepositoryInterface $customerRepository,
  52.         EntityRepositoryInterface $wabsAuthCustomerRepository,
  53.         EntityRepositoryInterface $customerGroupRepository,
  54.         AddressMatchingHelper $addressMatchingHelper,
  55.         Session $session,
  56.         DebugHelper $logger
  57.     ) {
  58.         $this->systemConfigService $systemConfigService;
  59.         $this->countryRepository $countryRepository;
  60.         $this->salutationRepository $salutationRepository;
  61.         $this->numberRangeValueGenerator $numberRangeValueGenerator;
  62.         $this->customerRepository $customerRepository;
  63.         $this->wabsAuthCustomerRepository $wabsAuthCustomerRepository;
  64.         $this->customerGroupRepository $customerGroupRepository;
  65.         $this->noAdvancedMode = (bool)$this->systemConfigService->get('WabsAuth.config.disableAdvancedMode');
  66.         $tmpGroupMapping $this->systemConfigService->get('WabsAuth.config.customerGroupMapping');
  67.         $tmpGroupMapping explode("\n"$tmpGroupMapping);
  68.         foreach ($tmpGroupMapping as $item) {
  69.             if (!empty($item)) {
  70.                 [$igmWord$groupName$femaleTranslation$maleTranslation] = explode(';'$item);
  71.                 if ($igmWord && $groupName) {
  72.                     $this->groupMapping[$igmWord] = [$groupName$femaleTranslation ?? ''$maleTranslation ?? ''];
  73.                 }
  74.             }
  75.         }
  76.         $this->session $session;
  77.         $this->addressMatchingHelper $addressMatchingHelper;
  78.         $this->logger $logger;
  79.     }
  80.     /**
  81.      * Returns the OpenID Connector
  82.      *
  83.      * @return
  84.      */
  85.     public function getProvider(): KeycloakProvider
  86.     {
  87.         $scopes $this->systemConfigService->get('WabsAuth.config.scopes');
  88.         $scopes explode(','$scopes);
  89.         $scopes array_map('trim'$scopes);
  90.         $algorithm = new Sha256();
  91.         return new KeycloakProvider([
  92.             'authServerUrl' => $this->systemConfigService->get('WabsAuth.config.authServerUrl'),
  93.             'realm' => $this->systemConfigService->get('WabsAuth.config.realm'),
  94.             'clientId' => $this->systemConfigService->get('WabsAuth.config.clientId'),
  95.             'clientSecret' => $this->systemConfigService->get('WabsAuth.config.clientSecret'),
  96.             'redirectUri' => $this->systemConfigService->get('WabsAuth.config.redirectUri'),
  97.             'encryptionAlgorithm' => $algorithm->algorithmId(),
  98.             'encryptionKey' => $this->systemConfigService->get('WabsAuth.config.publicKey'),
  99.             'scopes' => $scopes,
  100.         ]);
  101.     }
  102.     /**
  103.      * Returns a valid customer or throws an exception, if creation or update of customer failed.
  104.      *
  105.      * @param array $ownerData
  106.      * @param string $accessToken
  107.      * @param SalesChannelContext $context
  108.      * @return bool|CustomerEntity
  109.      * @throws Exception
  110.      */
  111.     public function getCustomer(array $ownerDatastring $accessTokenSalesChannelContext $context)
  112.     {
  113.         // check if user exists in database and return entity
  114.         $ownerData['email'] = $this->getOwnerMailAddress($ownerData);
  115.         if (!$ownerData['email']) {
  116.             $this->logger->debug('AuthHelper | No Email Address provided.');
  117.             throw new Exception('E-Mail Address not available.');
  118.         }
  119.         /** @var CustomerEntity $customer */
  120.         $customer $this->customerExists($ownerData$context);
  121.         if (!$customer) {
  122.             $customer $this->_register($ownerData$context);
  123.         }
  124.         if (!$customer->getActive()) {
  125.             $this->logger->debug('AuthHelper | Customer not active.');
  126.             throw new Exception('Customer Account deactivated.');
  127.         }
  128.         $customerGroups $this->getAllowedGroups($ownerData);
  129.         $customerGroupId $this->getCustomerGroupId($ownerData$customerGroups$context);
  130.         if (empty($customerGroupId)) {
  131.             $this->logger->debug('AuthHelper | No Valid Group for customer with number: ' $customer->getCustomerNumber());
  132.             throw new Exception('No valid Group for customer.');
  133.         }
  134.         if (empty($customerGroups)) {
  135.             $customerGroups[] = $customerGroups;
  136.         }
  137.         return $this->updateCustomer($customer$ownerData$customerGroupId$customerGroups$accessToken$context);
  138.     }
  139.     /**
  140.      * Returns the email of the customer from the given array.
  141.      *
  142.      * @param array $data
  143.      * @return string
  144.      */
  145.     protected function getOwnerMailAddress(array $data): string
  146.     {
  147.         if (filter_var($data['preferred_username'], FILTER_VALIDATE_EMAIL)) {
  148.             return $data['preferred_username'];
  149.         }
  150.         if (filter_var($data['igm_account_data']['accountName'], FILTER_VALIDATE_EMAIL)) {
  151.             return $data['igm_account_data']['accountName'];
  152.         }
  153.         return '';
  154.     }
  155.     /**
  156.      * Fetch current User by igm_uid
  157.      *
  158.      * @param array $data
  159.      * @param SalesChannelContext $context
  160.      *
  161.      * @return mixed|null
  162.      */
  163.     protected function customerExists(array $dataSalesChannelContext $context)
  164.     {
  165.         $criteria = new Criteria();
  166.         $criteria->addFilter(new EqualsFilter('customer.guest'0));
  167.         $criteria->addFilter(new EqualsFilter('pluginWabsAuthCustomer.igmUid'$data['igm_account_data']['relId']));
  168.         $result $this->customerRepository->search($criteria$context->getContext());
  169.         if ($result->count() !== 1) {
  170.             $this->logger->debug('AuthHelper | No customer with email address.');
  171.             return null;
  172.         }
  173.         return $result->first();
  174.     }
  175.     /**
  176.      * Registers a new customer in the system.
  177.      *
  178.      * @param array $data
  179.      * @param SalesChannelContext $context
  180.      *
  181.      * @return CustomerEntity
  182.      */
  183.     protected function _register(array $dataSalesChannelContext $context): CustomerEntity
  184.     {
  185.         /** get salutation uuid */
  186.         $gender 'mr';
  187.         if ($data['igm_account_data']['gender'] === 'W') {
  188.             $gender .= 's';
  189.         }
  190.         $criteria = new Criteria();
  191.         $criteria->addFilter(new EqualsFilter('salutationKey'$gender));
  192.         $salutation $this->salutationRepository->search($criteria$context)->first();
  193.         /** get country uuid | DE */
  194.         $criteria = new Criteria();
  195.         $criteria->addFilter(new EqualsFilter('active'true));
  196.         $criteria->addFilter(new EqualsFilter('iso''DE'));
  197.         $country $this->countryRepository->search($criteria$context)->first();
  198.         $customer = [
  199.             'id' => Uuid::randomHex(),
  200.             'customerNumber' => $this->numberRangeValueGenerator->getValue(
  201.                 $this->customerRepository->getDefinition()->getEntityName(),
  202.                 $context->getContext(),
  203.                 $context->getSalesChannel()->getId()
  204.             ),
  205.             'salesChannelId' => $context->getSalesChannel()->getId(),
  206.             'languageId' => $context->getContext()->getLanguageId(),
  207.             'groupId' => $context->getCurrentCustomerGroup()->getId(),
  208.             'defaultPaymentMethodId' => $context->getPaymentMethod()->getId(),
  209.             'salutationId' => $salutation->getId(),
  210.             'firstName' => $data['given_name'] ?? '',
  211.             'lastName' => $data['family_name'] ?? '',
  212.             'email' => $data['email'],
  213.             'title' => '',
  214.             'affiliateCode' => '',
  215.             'campaignCode' => '',
  216.             'active' => true,
  217.             'birthday' => '',
  218.             'guest' => false,
  219.             'firstLogin' => new DateTimeImmutable(),
  220.             'defaultShippingAddressId' => null,
  221.             'defaultBillingAddressId' => null,
  222.             'addresses' => [],
  223.         ];
  224.         $billingAddress = [];
  225.         $billingAddress['id'] = Uuid::randomHex();
  226.         $billingAddress['customerId'] = $customer['id'];
  227.         $billingAddress['firstName'] = $data['given_name'] ?? '';
  228.         $billingAddress['lastName'] = $data['family_name'] ?? '';
  229.         $billingAddress['salutationId'] = $salutation->getId();
  230.         /** @ToDo: Woher kommen diese Infos? */
  231.         /** Default: Aktuelle Daten des IGM Impressum */
  232.         $billingAddress['street'] = 'xxxxx';
  233.         $billingAddress['zipcode'] = 'xxxxx';
  234.         $billingAddress['city'] = 'xxxxx';
  235.         /** @ToDo: End */
  236.         $billingAddress['countryId'] = $country->getId();
  237.         $customer['defaultShippingAddressId'] = $billingAddress['id'];
  238.         $customer['defaultBillingAddressId'] = $billingAddress['id'];
  239.         $customer['addresses'][] = $billingAddress;
  240.         // Prüfen auf Funktionalität!
  241.         // $customer['pluginWabsAuthCustomer']['igmUid'] = $data['igm_account_data']['relId'];
  242.         // register new user with api data
  243.         $this->customerRepository->create([$customer], $context->getContext());
  244.         $criteria = new Criteria([$customer['id']]);
  245.         $criteria->addAssociation('addresses');
  246.         $criteria->addAssociation('salutation');
  247.         $customer $this->customerRepository->search($criteria$context->getContext())->first();
  248.         /** save relId */
  249.         $this->wabsAuthCustomerRepository->create([
  250.             [
  251.                 'customerId' => $customer->getId(),
  252.                 'igmUid' => $data['igm_account_data']['relId']
  253.             ]
  254.         ], $context->getContext());
  255.         /** @var CustomerEntity $customerEntity */
  256.         return $customer;
  257.     }
  258.     /**
  259.      * @param array $ownerData
  260.      * @return array
  261.      */
  262.     private function getAllowedGroups(array $ownerData): array
  263.     {
  264.         $igmGroups = [];
  265.         if (array_key_exists('igm_authorization_groups'$ownerData)) {
  266.             foreach ($ownerData['igm_authorization_groups'] as $igmGroup) {
  267.                 if (array_key_exists($igmGroup$this->groupMapping)) {
  268.                     $igmGroups[] = $this->groupMapping[$igmGroup];
  269.                 }
  270.             }
  271.         }
  272.         return $igmGroups;
  273.     }
  274.     /**
  275.      * Returns true, if the user has multiple groups to choose from.
  276.      * Else returns false.
  277.      *
  278.      * @param array $ownerData
  279.      * @param array $igmGroups
  280.      * @param SalesChannelContext $context
  281.      *
  282.      * @return string
  283.      * @throws Exception
  284.      */
  285.     public function getCustomerGroupId(
  286.         array $ownerData,
  287.         array $igmGroups,
  288.         SalesChannelContext $context
  289.     ): string {
  290.         $autoLoginGroup $this->session->get('oauth_auto_group');
  291.         if ($autoLoginGroup) {
  292.             $this->session->remove('oauth_auto_group');
  293.             $this->logger->debug('AuthHelper | Auto login is active (Group: ' $autoLoginGroup ').');
  294.             $this->logger->debug('AuthHelper | Available groups:' var_export($igmGroups,true));
  295.             foreach ($igmGroups as $igmGroup) {
  296.                 if (in_array($autoLoginGroup$igmGroup)) {
  297.                     $this->logger->debug('AuthHelper | Auto login returns ' $autoLoginGroup '.');
  298.                     return $this->loadCustomerGroupId($autoLoginGroup$context);
  299.                 }
  300.             }
  301.             if( $this->session->get('oauth_force_auto_group') ) {
  302.                 return '';
  303.             }
  304.         }
  305.         if (count($igmGroups) <= 0) {
  306.             if (!array_key_exists('igm_account_category'$ownerData) || empty($ownerData['igm_account_category'])) {
  307.                 $this->logger->debug('AuthHelper | getCustomerGroupId | No group transmitted.');
  308.                 return '';
  309.             }
  310.             $this->logger->debug('AuthHelper | getCustomerGroupId | igm_account_category used instead of groups.');
  311.             return $this->loadCustomerGroupId($ownerData['igm_account_category'], $context);
  312.         }
  313.         if (count($igmGroups) === 1) {
  314.             $this->logger->debug('AuthHelper | getCustomerGroupId | Customer had exactly one group so we use it.');
  315.             return $this->loadCustomerGroupId($igmGroups[0][0], $context);
  316.         }
  317.         $this->logger->debug('AuthHelper | getCustomerGroupId | Customer has multiple groups, so he is GC.');
  318.         return $this->loadCustomerGroupId('GC'$context);
  319.     }
  320.     /**
  321.      * Tries to set the group for the customer by simple name. If $groupName is not found, nothing happens.
  322.      *
  323.      * @param CustomerEntity $customer
  324.      * @param string $groupName
  325.      * @param SalesChannelContext $context
  326.      *
  327.      * @return CustomerEntity
  328.      */
  329.     public function loadCustomerGroupId(
  330.         string $groupName,
  331.         SalesChannelContext $context
  332.     ): string {
  333.         $criteria = new Criteria();
  334.         $possibleGroups $this->customerGroupRepository->search($criteria,
  335.             $context->getContext())->getEntities()->getElements();
  336.         if ($this->noAdvancedMode) {
  337.             $groupName 'Mitglied';
  338.         }
  339.         /** @var CustomerGroupEntity $group */
  340.         foreach ($possibleGroups as $group) {
  341.             if (strtolower($group->getName()) === strtolower($groupName)) {
  342.                 return $group->getId();
  343.             }
  344.         }
  345.         return '';
  346.     }
  347.     /**
  348.      * Updates the customer with the given $data array values.
  349.      *
  350.      * @param CustomerEntity $customerEntity
  351.      * @param array $data
  352.      * @param string $customerGroup
  353.      * @param array $igmGroups
  354.      * @param string $accessToken
  355.      * @param SalesChannelContext $context
  356.      *
  357.      * @return CustomerEntity
  358.      */
  359.     public function updateCustomer(
  360.         CustomerEntity $customerEntity,
  361.         array $data,
  362.         string $customerGroup,
  363.         array $igmGroups,
  364.         string $accessToken,
  365.         SalesChannelContext $context
  366.     ): CustomerEntity {
  367.         $customFieldData = [
  368.             'igm_session_token' => $data['igm_session_token'],
  369.             'igm_account_data' => $data['igm_account_data'],
  370.             'access_token' => $accessToken,
  371.             'igm_groups' => $igmGroups
  372.         ];
  373.         $this->logger->debug('AuthHelper | ARRAY customFieldData: ' json_encode($customFieldData));
  374.         $this->logger->debug('AuthHelper | ARRAY data: ' json_encode($customFieldData));
  375.         /** get salutation uuid */
  376.         $gender 'mr';
  377.         if ($data['igm_account_data']['gender'] === 'W') {
  378.             $gender .= 's';
  379.         }
  380.         $criteria = new Criteria();
  381.         $criteria->addFilter(new EqualsFilter('salutationKey'$gender));
  382.         $salutation $this->salutationRepository->search($criteria$context)->first();
  383.         $paymentMethodId $this->addressMatchingHelper->getAppropriatePaymentMethodId($customerGroup);
  384.         $addressUUID $this->addressMatchingHelper->createCustomerAddress(
  385.             $customerEntity,
  386.             $customerGroup,
  387.             $data['igm_session_token'],
  388.             $accessToken,
  389.             $data['igm_account_data'],
  390.             $context
  391.         );
  392.         if ($addressUUID === false) {
  393.             $this->organizationAddressFlag true;
  394.         }
  395.         $this->logger->debug('AuthHelper | customer update $addressUUID: ' json_encode($addressUUID));
  396.         $customer = [
  397.             'id' => $customerEntity->getId(),
  398.             'salutationId' => $salutation->getId(),
  399.             'firstName' => $data['igm_account_data']['firstname'] ?? $customerEntity->getFirstName(),
  400.             'lastName' => $data['igm_account_data']['lastname'] ?? $customerEntity->getLastName(),
  401.             'email' => $data['igm_account_data']['accountName'] ?? $customerEntity->getEmail(),
  402.             'customFields' => $customFieldData,
  403.             'groupId' => $customerGroup,
  404.             'lastLogin' => new DateTimeImmutable(),
  405.             'defaultPaymentMethodId' => $paymentMethodId,
  406.             'lastPaymentMethodId' => $paymentMethodId,
  407.         ];
  408.         if (!empty($addressUUID)) {
  409.             $customer['defaultShippingAddressId'] = $addressUUID;
  410.             $customer['defaultBillingAddressId'] = $addressUUID;
  411.         }
  412.         $this->logger->debug('AuthHelper | ARRAY customer update data: ' json_encode($customer));
  413.         // update user with api data
  414.         $this->customerRepository->update([$customer], $context->getContext());
  415.         $customerEntity $this->addressMatchingHelper->reloadCustomer($customerEntity$context);
  416.         $this->addressMatchingHelper->deleteUnnecessaryAddresses($customerEntity$context);
  417.         return $customerEntity;
  418.     }
  419.     /**
  420.      * Loads the customer group of the user and adds them to the customer
  421.      *
  422.      * @param CustomerEntity $customer
  423.      * @param SalesChannelContext $context
  424.      * @return CustomerEntity|null
  425.      */
  426.     public function loadCustomerGroupToCustomer(CustomerEntity $customerSalesChannelContext $context): ?CustomerEntity
  427.     {
  428.         $criteria = new Criteria();
  429.         $criteria->addFilter(new EqualsFilter('id'$customer->getGroupId()));
  430.         $customerGroup $this->customerGroupRepository->search($criteria$context->getContext())->first();
  431.         if ($customerGroup === null) {
  432.             $this->logger->debug('AuthHelper | No customer group could be loaded to customer.');
  433.             return $customer;
  434.         }
  435.         $customer->setGroup($customerGroup);
  436.         return $customer;
  437.     }
  438.     /**
  439.      * Checks if user in the GC group that is used to select groups. Checks then if the user has the
  440.      * $selectedGroup added to the account on login and therefore the permission to use the $selectedGroup.
  441.      *
  442.      * @param string $selectedGroup
  443.      * @param CustomerEntity $customer
  444.      * @return bool
  445.      */
  446.     public function checkAllowedGroups(string $selectedGroupCustomerEntity $customer): bool
  447.     {
  448.         $customerGroup $customer->getGroup();
  449.         if ($this->noAdvancedMode) {
  450.             return true;
  451.         }
  452.         if ($customerGroup === null || $customerGroup->getName() !== 'GC') {
  453.             $this->logger->debug('AuthHelper | Customer has no group and is therefore not allowed to login.');
  454.             return false;
  455.         }
  456.         $customerCustomFields $customer->getCustomFields();
  457.         if (!array_key_exists('igm_groups'$customerCustomFields)) {
  458.             $this->logger->debug('AuthHelper | igm_groups array key is missing in custom fields.');
  459.             return false;
  460.         }
  461.         $possibleGroups $customerCustomFields['igm_groups'];
  462.         foreach ($possibleGroups as $group) {
  463.             if (in_array($selectedGroup$group)) {
  464.                 return true;
  465.             }
  466.         }
  467.         $this->logger->debug('AuthHelper | The customer group is not in the allowed groups.');
  468.         return false;
  469.     }
  470. }