diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index 28995439..89dd0002 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist @@ -38,6 +38,10 @@ parameters: # PCRE as accepted by preg_match (http://php.net/preg_match). mobile_app_user_agent_pattern: "/^.*$/" + # Flag to use Firebse as fallback when GCM fails with a MismatchSenderId. + # This is used to handle the push notification method rollover from GCM to Firebase. + use_firebase_fallback_for_gcm: true + # Options for the tiqr library tiqr_library_options: general: @@ -57,6 +61,8 @@ parameters: apns: certificate: 'absolute path to certificate' environment: production + firebase: + apikey: 'Your Firebase API KEY' accountblocking: maxAttempts: 5 storage: diff --git a/composer.json b/composer.json index 9b7643ed..b9ae1e33 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "phpunit/phpunit": "^5.7", "sebastian/phpcpd": "^3.0", "sensio/generator-bundle": "^3.0", - "sensiolabs/security-checker": "^4.1", + "sensiolabs/security-checker": "^5.0", "squizlabs/php_codesniffer": "^3.1", "symfony/phpunit-bridge": "^3.0" }, @@ -95,7 +95,7 @@ "phpunit": "vendor/bin/phpunit tests", "behat": ["vendor/bin/behat --config behat.yml --tags '~skip'"], - "security-tests": "vendor/bin/security-checker security:check --end-point=http://security.sensiolabs.org/check_lock", + "security-tests": "vendor/bin/security-checker security:check", "coverage": [ "@phpunit-coverage", diff --git a/composer.lock b/composer.lock index cf508296..0b1b745e 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "248f98a1778700b5f817d9805d9c93bc", + "content-hash": "626f229659f60927420af6f59a39ea6d", "packages": [ { "name": "beberlei/assert", @@ -1392,21 +1392,21 @@ }, { "name": "sensio/distribution-bundle", - "version": "v5.0.22", + "version": "v5.0.24", "source": { "type": "git", "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", - "reference": "209b11f8cee5bf71986dd703e45e27d3ed7a6d15" + "reference": "59eac70f15f97ee945924948a6f5e2f6f86b7a4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/209b11f8cee5bf71986dd703e45e27d3ed7a6d15", - "reference": "209b11f8cee5bf71986dd703e45e27d3ed7a6d15", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/59eac70f15f97ee945924948a6f5e2f6f86b7a4b", + "reference": "59eac70f15f97ee945924948a6f5e2f6f86b7a4b", "shasum": "" }, "require": { "php": ">=5.3.9", - "sensiolabs/security-checker": "~3.0|~4.0", + "sensiolabs/security-checker": "~5.0", "symfony/class-loader": "~2.3|~3.0", "symfony/config": "~2.3|~3.0", "symfony/dependency-injection": "~2.3|~3.0", @@ -1440,7 +1440,7 @@ "configuration", "distribution" ], - "time": "2018-06-07T06:22:12+00:00" + "time": "2018-12-14T17:36:15+00:00" }, { "name": "sensio/framework-extra-bundle", @@ -1514,20 +1514,21 @@ }, { "name": "sensiolabs/security-checker", - "version": "v4.1.8", + "version": "v5.0.3", "source": { "type": "git", "url": "https://github.com/sensiolabs/security-checker.git", - "reference": "dc270d5fec418cc6ac983671dba5d80ffaffb142" + "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/dc270d5fec418cc6ac983671dba5d80ffaffb142", - "reference": "dc270d5fec418cc6ac983671dba5d80ffaffb142", + "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/46be3f58adac13084497961e10eed9a7fb4d44d1", + "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1", "shasum": "" }, "require": { "composer/ca-bundle": "^1.0", + "php": ">=5.5.9", "symfony/console": "~2.7|~3.0|~4.0" }, "bin": [ @@ -1536,12 +1537,12 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "5.0-dev" } }, "autoload": { - "psr-0": { - "SensioLabs\\Security": "" + "psr-4": { + "SensioLabs\\Security\\": "SensioLabs/Security" } }, "notification-url": "https://packagist.org/downloads/", @@ -1555,7 +1556,7 @@ } ], "description": "A security checker for your composer.lock", - "time": "2018-02-28T22:10:01+00:00" + "time": "2018-12-19T17:14:59+00:00" }, { "name": "simplesamlphp/saml2", @@ -2627,20 +2628,22 @@ }, { "name": "tiqr/tiqr-server-libphp", - "version": "v1.1.6", + "version": "v1.1.10", "source": { "type": "git", "url": "https://github.com/SURFnet/tiqr-server-libphp.git", - "reference": "9d3fc594c5070c0427f998bd27f6be71e99a3f36" + "reference": "70fa11c41d68be4cde040902eae202dcdea369b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SURFnet/tiqr-server-libphp/zipball/9d3fc594c5070c0427f998bd27f6be71e99a3f36", - "reference": "9d3fc594c5070c0427f998bd27f6be71e99a3f36", + "url": "https://api.github.com/repos/SURFnet/tiqr-server-libphp/zipball/70fa11c41d68be4cde040902eae202dcdea369b5", + "reference": "70fa11c41d68be4cde040902eae202dcdea369b5", "shasum": "" }, "require": { + "ext-curl": "*", "ext-gd": "*", + "ext-json": "*", "kairos/phpqrcode": "~1.0", "zendframework/zendframework1": "~1.12" }, @@ -2650,7 +2653,7 @@ "BSD-3-Clause" ], "description": "php library for tiqr authentication.", - "time": "2018-09-04T11:58:24+00:00" + "time": "2019-02-28T15:49:53+00:00" }, { "name": "twig/twig", @@ -4671,6 +4674,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2017-06-30T09:13:00+00:00" }, { diff --git a/src/AppBundle/Controller/AuthenticationController.php b/src/AppBundle/Controller/AuthenticationController.php index b452d0ae..ce4938da 100644 --- a/src/AppBundle/Controller/AuthenticationController.php +++ b/src/AppBundle/Controller/AuthenticationController.php @@ -48,6 +48,10 @@ class AuthenticationController extends Controller private $authenticationRateLimitService; private $userRepository; private $stateHandler; + /** + * @var bool + */ + private $useFirebaseFallbackForGcm; public function __construct( AuthenticationService $authenticationService, @@ -55,7 +59,8 @@ public function __construct( TiqrServiceInterface $tiqrService, TiqrUserRepositoryInterface $userRepository, AuthenticationRateLimitServiceInterface $authenticationRateLimitService, - LoggerInterface $logger + LoggerInterface $logger, + $useFirebaseFallbackForGcm ) { $this->authenticationService = $authenticationService; $this->stateHandler = $stateHandler; @@ -63,6 +68,7 @@ public function __construct( $this->logger = $logger; $this->authenticationRateLimitService = $authenticationRateLimitService; $this->userRepository = $userRepository; + $this->useFirebaseFallbackForGcm = $useFirebaseFallbackForGcm; } /** @@ -317,13 +323,7 @@ public function authenticationNotificationAction() $notificationType = $user->getNotificationType(); $notificationAddress = $user->getNotificationAddress(); if ($notificationType && $notificationAddress) { - $this->logger->info(sprintf( - 'Sending client notification for type "%s" and address "%s"', - $notificationType, - $notificationAddress - )); - $result = $this->tiqrService->sendNotification($notificationType, $notificationAddress); - $this->logNotificationResponse($result, $notificationType, $notificationAddress); + $result = $this->sendNotification($notificationType, $notificationAddress); if ($result) { return $this->generateNotificationResponse('success'); } @@ -350,35 +350,6 @@ private function handleInvalidResponse(TiqrUserInterface $user, $response, Logge ]); } - /** - * @param boolean $result - * @param string $notificationType - * @param string $notificationAddress - */ - private function logNotificationResponse($result, $notificationType, $notificationAddress) - { - if ($result) { - $this->logger->info(sprintf( - 'Push notification successfully send for type "%s" and address "%s"', - $notificationType, - $notificationAddress - )); - - return; - } - - $this->logger->warning( - sprintf( - 'Failed to send push notification for type "%s" and address "%s"', - $notificationType, - $notificationAddress - ), - [ - 'error_info' => $this->tiqrService->getNotificationError(), - ] - ); - } - private function showUserIsBlockedErrorPage($isBlockedPermanently) { $exception = new UserTemporarilyBlockedException(); @@ -394,4 +365,64 @@ private function showUserIsBlockedErrorPage($isBlockedPermanently) ] ); } + + /** + * @param $notificationType + * @param $notificationAddress + * @return bool + */ + private function sendNotification($notificationType, $notificationAddress) + { + $this->logger->notice(sprintf( + 'Sending client notification for type "%s" and address "%s"', + $notificationType, + $notificationAddress + )); + + $result = $this->tiqrService->sendNotification($notificationType, $notificationAddress); + if (!$result + && $notificationType == 'GCM' + && $this->useFirebaseFallbackForGcm + && $this->tiqrService->getNotificationError()['message'] == 'MismatchSenderId' + ) { + // Retry with FCM if GCM + $this->logger->notice( + sprintf( + 'Failed to send push notification for type "%s" and address "%s" retrying with FCM', + $notificationType, + $notificationAddress + ), + [ + 'error_info' => $this->tiqrService->getNotificationError(), + ] + ); + + $notificationType = 'FCM'; + $result = $this->tiqrService->sendNotification($notificationType, $notificationAddress); + } + + if (!$result) { + $this->logger->warning( + sprintf( + 'Failed to send push notification for type "%s" and address "%s"', + $notificationType, + $notificationAddress + ), + [ + 'error_info' => $this->tiqrService->getNotificationError(), + ] + ); + return false; + } + + $this->logger->notice( + sprintf( + 'Successfully send push notification for type "%s" and address "%s"', + $notificationType, + $notificationAddress + ) + ); + + return true; + } } diff --git a/src/AppBundle/DependencyInjection/Configuration.php b/src/AppBundle/DependencyInjection/Configuration.php index c6caa1ef..423d3721 100644 --- a/src/AppBundle/DependencyInjection/Configuration.php +++ b/src/AppBundle/DependencyInjection/Configuration.php @@ -150,6 +150,16 @@ private function createLibraryConfig() ->isRequired() ->info(<<end() + ->end() + ->end() + ->arrayNode('firebase') + ->children() + ->scalarNode('apikey') + ->info(<<end() diff --git a/src/AppBundle/Resources/config/services.yml b/src/AppBundle/Resources/config/services.yml index 8a02c511..3337773f 100644 --- a/src/AppBundle/Resources/config/services.yml +++ b/src/AppBundle/Resources/config/services.yml @@ -1,4 +1,3 @@ -services: services: AppBundle\Twig\GsspExtension: tags: [twig.extension] @@ -55,5 +54,9 @@ services: - '@logger' AppBundle\Service\UserAgentMatcher: - arguments: - - '%mobile_app_user_agent_pattern%' + bind: + $pattern: '%mobile_app_user_agent_pattern%' + + AppBundle\Controller\AuthenticationController: + bind: + $useFirebaseFallbackForGcm: '%use_firebase_fallback_for_gcm%' \ No newline at end of file diff --git a/src/AppBundle/Tiqr/TiqrConfiguration.php b/src/AppBundle/Tiqr/TiqrConfiguration.php index 08cfc5c7..f22ff9c2 100644 --- a/src/AppBundle/Tiqr/TiqrConfiguration.php +++ b/src/AppBundle/Tiqr/TiqrConfiguration.php @@ -70,6 +70,11 @@ public function __construct($configuration) $this->options['gcm.application'] = $configuration['library']['gcm']['application']; } + if (isset($configuration['library']['firebase'])) { + Assertion::string($configuration['library']['firebase']['apikey']); + $this->options['firebase.apikey'] = $configuration['library']['firebase']['apikey']; + } + if (isset($configuration['accountblocking'][self::MAX_ATTEMPTS])) { Assertion::digit($configuration['accountblocking'][self::MAX_ATTEMPTS]); $this->options[self::MAX_ATTEMPTS] = $configuration['accountblocking'][self::MAX_ATTEMPTS];