diff --git a/app/Resources/translations/messages.en_GB.xliff b/app/Resources/translations/messages.en_GB.xliff index 1307c30e1..2ae9e2680 100644 --- a/app/Resources/translations/messages.en_GB.xliff +++ b/app/Resources/translations/messages.en_GB.xliff @@ -17,6 +17,21 @@ button.logout Sign out + + Form/Type/SendSmsChallengeType.php + country code + country code + + + Resources/views/translations.twig + locale.en_GB + English + + + Resources/views/translations.twig + locale.nl_NL + Nederlands + views/Exception/error.html.twig views/Exception/error404.html.twig @@ -84,6 +99,16 @@ ss.error.title Error + + SelfServiceBundle/Controller/LocaleControl.php + ss.flash.error_while_switching_locale + Due to an unknown reason, switching locales failed. + + + SelfServiceBundle/Controller/LocaleControl.php + ss.flash.invalid_switch_locale_form + Due to an unknown reason, switching locales failed. + Form/Type/RevokeSecondFactorType.php ss.form.ss_revoke_second_factor.cancel @@ -124,6 +149,46 @@ ss.form.ss_verify_sms_challenge.text.challenge Code + + Resources/views/translations.twig + ss.prove_phone_possession.challenge_expired + Your code has expired. Please request a new code. + + + Resources/views/translations.twig + ss.prove_phone_possession.challenge_request_limit_reached + You have exceeded the limit of three codes; you can no longer request any more codes. Contact your helpdesk or try again later. + + + Resources/views/translations.twig + ss.prove_phone_possession.challenge_response_incorrect + The code you entered does not match the code you received. + + + Resources/views/translations.twig + ss.prove_phone_possession.incorrect_challenge_response + The code you entered does not match. Please try again or request a new code. + + + Resources/views/translations.twig + ss.prove_phone_possession.proof_of_possession_failed + The token could not be created due to unknown reasons. + + + Resources/views/translations.twig + ss.prove_phone_possession.send_sms_challenge_failed + Sending the code by text message failed. + + + Resources/views/translations.twig + ss.prove_phone_possession.too_many_attempts + You have exceeded the limit of ten attempts; you can no longer attempt verification of any more codes. Contact your helpdesk or try again later. + + + Resources/views/translations.twig + ss.prove_yubikey_possession.proof_of_possession_failed + The token could not be created due to unknown reasons. + views/Registration/emailVerificationEmailSent.html.twig ss.registration.email_verification_email_sent.text.email_verification_has_been_sent @@ -209,11 +274,6 @@ ss.registration.registration_email_sent.title.list_of_ras Location to activate your token - - Resources/views/translations.twig - ss.registration.selector.alert.no_second_factors_yet - You do not have any security tokens yet. - Resources/views/translations.twig ss.registration.selector.sms.alt @@ -378,7 +438,7 @@ Resources/views/translations.twig ss.second_factor.list.text.unverified - ss.second_factor.list.text.unverified + You are still to prove your possession of the following token. Resources/views/translations.twig @@ -472,6 +532,21 @@ An e-mail with your activation code has been sent to the e-mail address %email%. ss.second_factor_list.header.type Token + + Resources/views/translations.twig + ss.verify_yubikey_command.otp.otp_invalid + This Yubikey code was invalid. Please try again. + + + Resources/views/translations.twig + ss.verify_yubikey_command.otp.verification_error + The verification of the Yubikey code failed due to unknown reasons. Try again. + + + Form/Type/SwitchLocaleType.php + stepup_middleware_client.form.switch_locale.switch + Switch + diff --git a/app/Resources/translations/messages.nl_NL.xliff b/app/Resources/translations/messages.nl_NL.xliff index 476f4de7e..0ef4f9f39 100644 --- a/app/Resources/translations/messages.nl_NL.xliff +++ b/app/Resources/translations/messages.nl_NL.xliff @@ -17,6 +17,21 @@ button.logout Uitloggen + + Form/Type/SendSmsChallengeType.php + country code + country code + + + Resources/views/translations.twig + locale.en_GB + English + + + Resources/views/translations.twig + locale.nl_NL + Nederlands + views/Exception/error.html.twig views/Exception/error404.html.twig @@ -84,6 +99,16 @@ ss.error.title Foutmelding + + SelfServiceBundle/Controller/LocaleControl.php + ss.flash.error_while_switching_locale + Due to an unknown reason, switching locales failed. + + + SelfServiceBundle/Controller/LocaleControl.php + ss.flash.invalid_switch_locale_form + Due to an unknown reason, switching locales failed. + Form/Type/RevokeSecondFactorType.php ss.form.ss_revoke_second_factor.cancel @@ -124,6 +149,46 @@ ss.form.ss_verify_sms_challenge.text.challenge Code + + Resources/views/translations.twig + ss.prove_phone_possession.challenge_expired + Uw code is verlopen. Vraag een nieuwe code aan. + + + Resources/views/translations.twig + ss.prove_phone_possession.challenge_request_limit_reached + U heeft de limiet van drie codes bereikt; u kunt geen codes meer aanvragen. Neem contact op met uw helpdesk of probeer het later nog eens. + + + Resources/views/translations.twig + ss.prove_phone_possession.challenge_response_incorrect + De code die je ingevoerd hebt komt niet overeen met de code die je hebt ontvangen. + + + Resources/views/translations.twig + ss.prove_phone_possession.incorrect_challenge_response + De ingevoerde code is onjuist. Probeer het nog eens, of vraag een nieuwe code op. + + + Resources/views/translations.twig + ss.prove_phone_possession.proof_of_possession_failed + Het token kon wegens een onbekende reden niet aangemaakt worden. + + + Resources/views/translations.twig + ss.prove_phone_possession.send_sms_challenge_failed + Het versturen van de code per SMS is mislukt. + + + Resources/views/translations.twig + ss.prove_phone_possession.too_many_attempts + U heeft de limiet van tien pogingen bereikt; u kunt geen codes meer verifiëren. Neem contact op met uw helpdesk of probeer het later nog eens. + + + Resources/views/translations.twig + ss.prove_yubikey_possession.proof_of_possession_failed + Het token kon wegens een onbekende reden niet aangemaakt worden. + views/Registration/emailVerificationEmailSent.html.twig ss.registration.email_verification_email_sent.text.email_verification_has_been_sent @@ -209,11 +274,6 @@ ss.registration.registration_email_sent.title.list_of_ras Locatie om je token te activeren - - Resources/views/translations.twig - ss.registration.selector.alert.no_second_factors_yet - Je hebt nog geen token geregistreerd. - Resources/views/translations.twig ss.registration.selector.sms.alt @@ -377,12 +437,12 @@ Resources/views/translations.twig ss.second_factor.list.text.unverified - ss.second_factor.list.text.unverified + Van het volgende token moet het bezit nog bewezen worden. Resources/views/translations.twig ss.second_factor.list.text.verified - Het volgende token is geregistreerd voor jouw account, maar nog niet geactiveerd. + Het volgende token is geregistreerd voor jouw account, maar nog niet geactiveerd. Er is een e-mail met activatiecode gestuurd naar het e-mailadres %email%. Volg de instructies uit de e-mail om je token te activeren. @@ -471,6 +531,21 @@ Er is een e-mail met activatiecode gestuurd naar het e-mailadres %email%. Volg d ss.second_factor_list.header.type Token + + Resources/views/translations.twig + ss.verify_yubikey_command.otp.otp_invalid + Deze Yubikey code was ongeldig. Probeer het nog eens. + + + Resources/views/translations.twig + ss.verify_yubikey_command.otp.verification_error + Het verifiëren van de Yubikey-code is wegens een onbekende reden niet gelukt. Probeer het opnieuw. + + + Form/Type/SwitchLocaleType.php + stepup_middleware_client.form.switch_locale.switch + Vertalen + diff --git a/app/Resources/translations/validators.en_GB.xliff b/app/Resources/translations/validators.en_GB.xliff index f33b44771..1d68d3cd5 100644 --- a/app/Resources/translations/validators.en_GB.xliff +++ b/app/Resources/translations/validators.en_GB.xliff @@ -182,53 +182,13 @@ middleware_client.dto.vetted_second_factor.type.must_not_be_blank middleware_client.dto.vetted_second_factor.type.must_not_be_blank - - Resources/views/translations.twig - ss.prove_phone_possession.challenge_expired - Your code has expired. Please request a new code. - - - Resources/views/translations.twig - ss.prove_phone_possession.challenge_request_limit_reached - You have exceeded the limit of three codes; you can no longer request any more codes. Contact your helpdesk or try again later. - - - Resources/views/translations.twig - ss.prove_phone_possession.challenge_response_incorrect - ss.prove_phone_possession.challenge_response_incorrect - - - Resources/views/translations.twig - ss.prove_phone_possession.incorrect_challenge_response - The code you entered does not match. Please try again or request a new code. - - - Resources/views/translations.twig - ss.prove_phone_possession.proof_of_possession_failed - The token could not be created due to unknown reasons. - - - Resources/views/translations.twig - ss.prove_phone_possession.send_sms_challenge_failed - Sending the code by text message failed. - - - Resources/views/translations.twig - ss.prove_phone_possession.too_many_attempts - You have exceeded the limit of ten attempts; you can no longer attempt verification of any more codes. Contact your helpdesk or try again later. - - - Resources/views/translations.twig - ss.prove_yubikey_possession.proof_of_possession_failed - The token could not be created due to unknown reasons. - ss.revoke_own_second_factor_command.identity_id.must_be_string - ss.revoke_own_second_factor_command.identity_id.must_be_string + Identity ID must be a string ss.revoke_own_second_factor_command.second_factor_id.must_be_string - ss.revoke_own_second_factor_command.second_factor_id.must_be_string + Second factor ID must be a string ss.send_sms_challenge_command.recipient.may_not_be_empty @@ -242,18 +202,6 @@ ss.send_sms_challenge_command.recipient.must_be_string SMS challenge recipient must be string. - - ss.send_sms_command.recipient.may_not_be_empty - SMS recipient may not be empty. - - - ss.send_sms_command.recipient.must_be_string - SMS recipient must be string. - - - ss.send_sms_command.recipient.must_consist_of_digits - SMS recipient may consist of digits only. - ss.verify_sms_challenge_command.challenge.may_not_be_empty SMS challenge may not be empty. @@ -270,15 +218,25 @@ ss.verify_yubikey_command.otp.must_be_string Yubikey OTP must be string. - - Resources/views/translations.twig - ss.verify_yubikey_command.otp.otp_invalid - This Yubikey code was invalid. Please try again. + + stepup.send_sms_command.recipient.may_not_be_empty + Please enter your phone number + + + stepup.send_sms_command.recipient.must_be_string + SMS recipient must be a string + + + stepup.send_sms_command.recipient.must_consist_of_digits + Your phone number may only consist of digits + + + stepup.verify_possession_of_phone_command.challenge.may_not_be_empty + Please enter the code you received - - Resources/views/translations.twig - ss.verify_yubikey_command.otp.verification_error - The verification of the Yubikey code failed due to unknown reasons. Try again. + + stepup.verify_possession_of_phone_command.challenge.must_be_string + SMS challenge must be a string diff --git a/app/Resources/translations/validators.nl_NL.xliff b/app/Resources/translations/validators.nl_NL.xliff index 48d76b9ee..12ac5bb5b 100644 --- a/app/Resources/translations/validators.nl_NL.xliff +++ b/app/Resources/translations/validators.nl_NL.xliff @@ -182,53 +182,13 @@ middleware_client.dto.vetted_second_factor.type.must_not_be_blank middleware_client.dto.vetted_second_factor.type.must_not_be_blank - - Resources/views/translations.twig - ss.prove_phone_possession.challenge_expired - Uw code is verlopen. Vraag een nieuwe code aan. - - - Resources/views/translations.twig - ss.prove_phone_possession.challenge_request_limit_reached - U heeft de limiet van drie codes bereikt; u kunt geen codes meer aanvragen. Neem contact op met uw helpdesk of probeer het later nog eens. - - - Resources/views/translations.twig - ss.prove_phone_possession.challenge_response_incorrect - ss.prove_phone_possession.challenge_response_incorrect - - - Resources/views/translations.twig - ss.prove_phone_possession.incorrect_challenge_response - De ingevoerde code is onjuist. Probeer het nog eens, of vraag een nieuwe code op. - - - Resources/views/translations.twig - ss.prove_phone_possession.proof_of_possession_failed - Het token kon wegens een onbekende reden niet aangemaakt worden. - - - Resources/views/translations.twig - ss.prove_phone_possession.send_sms_challenge_failed - Het versturen van de code per SMS is mislukt. - - - Resources/views/translations.twig - ss.prove_phone_possession.too_many_attempts - U heeft de limiet van tien pogingen bereikt; u kunt geen codes meer verifiëren. Neem contact op met uw helpdesk of probeer het later nog eens. - - - Resources/views/translations.twig - ss.prove_yubikey_possession.proof_of_possession_failed - Het token kon wegens een onbekende reden niet aangemaakt worden. - ss.revoke_own_second_factor_command.identity_id.must_be_string - ss.revoke_own_second_factor_command.identity_id.must_be_string + Identity ID must be a string ss.revoke_own_second_factor_command.second_factor_id.must_be_string - ss.revoke_own_second_factor_command.second_factor_id.must_be_string + Second factor ID must be a string ss.send_sms_challenge_command.recipient.may_not_be_empty @@ -242,18 +202,6 @@ ss.send_sms_challenge_command.recipient.must_be_string SMS challenge recipient must be string. - - ss.send_sms_command.recipient.may_not_be_empty - SMS recipient may not be empty. - - - ss.send_sms_command.recipient.must_be_string - SMS recipient must be string. - - - ss.send_sms_command.recipient.must_consist_of_digits - SMS recipient may consist of digits only. - ss.verify_sms_challenge_command.challenge.may_not_be_empty SMS challenge may not be empty. @@ -270,15 +218,25 @@ ss.verify_yubikey_command.otp.must_be_string Yubikey OTP must be string. - - Resources/views/translations.twig - ss.verify_yubikey_command.otp.otp_invalid - Deze Yubikey code was ongeldig. Probeer het nog eens. + + stepup.send_sms_command.recipient.may_not_be_empty + Vul alstublieft het telefoonnummer in + + + stepup.send_sms_command.recipient.must_be_string + SMS recipient must be a string + + + stepup.send_sms_command.recipient.must_consist_of_digits + Het telefoonnummer mag enkel uit cijfers bestaan + + + stepup.verify_possession_of_phone_command.challenge.may_not_be_empty + Vul alstublieft de code in die u heeft ontvangen - - Resources/views/translations.twig - ss.verify_yubikey_command.otp.verification_error - Het verifiëren van de Yubikey-code is wegens een onbekende reden niet gelukt. Probeer het opnieuw. + + stepup.verify_possession_of_phone_command.challenge.must_be_string + SMS challenge must be a string diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig index bdb173d72..b7800e196 100644 --- a/app/Resources/views/base.html.twig +++ b/app/Resources/views/base.html.twig @@ -8,6 +8,7 @@ {% block head_style %} {% stylesheets filter='less' '@SurfnetStepupSelfServiceSelfServiceBundle/Resources/public/less/style.less' + '@SurfnetStepupBundle/Resources/public/less/stepup.less' %} {% endstylesheets %} @@ -32,6 +33,11 @@ {{ 'button.logout'|trans }} + {% set locale_switcher = stepup_locale_switcher(app.request.locale, 'ss_switch_locale', {'return-url': app.request.uri}) %} + {{ form_start(locale_switcher, { attr: { class: 'form-inline' }}) }} + {{ form_widget(locale_switcher.locale) }} + {{ form_widget(locale_switcher.switch) }} + {{ form_end(locale_switcher) }}
{% endif %} diff --git a/app/config/config.yml b/app/config/config.yml index 26369b79f..9277fbfe8 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -16,7 +16,8 @@ framework: validation: { enable_annotations: true } templating: engines: ['twig'] - assets_version: %asset_version% + assets: + version: %asset_version% default_locale: "%default_locale%" trusted_hosts: ~ trusted_proxies: %trusted_proxies% @@ -89,17 +90,6 @@ monolog: publisher: { hostname: %graylog_hostname% } formatter: surfnet_stepup.monolog.full_message_exception_gelf_message_formatter -surfnet_stepup_self_service_self_service: - sms: - originator: %sms_originator% - otp_expiry_interval: %sms_otp_expiry_interval% - maximum_otp_requests: %sms_maximum_otp_requests% - gateway_api: - url: %gateway_api_url% - credentials: - username: %gateway_api_username% - password: %gateway_api_password% - mopa_bootstrap: form: show_legend: false @@ -145,6 +135,15 @@ surfnet_stepup: loa1: %stepup_loa_loa1% loa2: %stepup_loa_loa2% loa3: %stepup_loa_loa3% + sms: + originator: %sms_originator% + otp_expiry_interval: %sms_otp_expiry_interval% + maximum_otp_requests: %sms_maximum_otp_requests% + gateway_api: + url: %gateway_api_url% + credentials: + username: %gateway_api_username% + password: %gateway_api_password% jms_translation: locales: %locales% diff --git a/app/config/config_dev.yml b/app/config/config_dev.yml index e40ee78bc..cfe93935f 100644 --- a/app/config/config_dev.yml +++ b/app/config/config_dev.yml @@ -11,23 +11,45 @@ web_profiler: toolbar: "%debug_toolbar%" intercept_redirects: "%debug_redirects%" +# Be careful not to remove the prod-signaler handler, which overwrites +# the prod-signaler handler defined in logging.yml. The handler defined +# in logging.yml disables bubbling which means that none of the handlers +# below are invoked. Since the current dev setup is incompatible with the +# prod setup defined in logging.yml, this means we won't see any logs in +# the infrastructure currently used (graylog). Overwriting the handler +# here resolves that and reinstates the dev logging setup. +# +# this configuration must be replaced to reflect production setup +# see https://www.pivotaltracker.com/story/show/96056010 +# monolog: handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.debug.log" - level: debug - console: - type: console - bubble: false - # uncomment to get logging in your browser - # you may have to allow bigger header sizes in your Web server configuration - #firephp: - # type: firephp - # level: info - #chromephp: - # type: chromephp - # level: info + prod-signaler: + type: group + members: + - main_graylog + - main_logfile + - main_debuglog + main_graylog: + type: buffer + handler: graylog + level: NOTICE + main_logfile: + type: stream + handler: logfile + level: NOTICE + path: %kernel.logs_dir%/%kernel.environment%.log + formatter: surfnet_stepup.monolog.json_formatter + main_debuglog: + type: stream + handler: logfile + level: DEBUG + path: "%kernel.logs_dir%/%kernel.environment%.debug.log" + formatter: surfnet_stepup.monolog.json_formatter + graylog: + type: gelf + publisher: { hostname: %graylog_hostname% } + formatter: surfnet_stepup.monolog.full_message_exception_gelf_message_formatter assetic: use_controller: "%use_assetic_controller%" diff --git a/app/config/config_prod.yml b/app/config/config_prod.yml index 4eb2c8ce6..54fc4f28f 100644 --- a/app/config/config_prod.yml +++ b/app/config/config_prod.yml @@ -5,25 +5,3 @@ imports: # validation: # cache: apc -monolog: - handlers: - prod-signaler: - type: fingers_crossed - action_level: ERROR - handler: prod_main - bubble: false # if we handle it, nothing else should - prod_main: - type: group - members: - - main_graylog - - main_logfile - - buffered_mailer - buffered_mailer: - type: buffer - handler: swift - swift: - type: swift_mailer - # these email addresses should be configurable and configured correctly - from_email: stepup-error@surfnet.nl - to_email: error@example.com - subject: "[StepUp][SelfService][%kernel.environment%] An Error Occurred!" diff --git a/app/config/logging.yml b/app/config/logging.yml index b102c7f1d..945481941 100644 --- a/app/config/logging.yml +++ b/app/config/logging.yml @@ -1,20 +1,13 @@ monolog: handlers: - main: - type: group - members: - - main_graylog - - main_logfile - main_graylog: - type: buffer - handler: graylog - level: NOTICE - main_logfile: - type: stream - handler: logfile - level: NOTICE - path: %kernel.logs_dir%/%kernel.environment%.log - graylog: - type: gelf - publisher: { hostname: %graylog_hostname% } - formatter: surfnet_stepup.monolog.full_message_exception_gelf_message_formatter + prod-signaler: + type: fingers_crossed + action_level: ERROR + passthru_level: NOTICE # this means that all message of level NOTICE or higher are always logged + handler: main_syslog + bubble: false # if we handle it, nothing else should + main_syslog: + type: syslog + ident: stepup-selfservice + facility: user + formatter: surfnet_stepup.monolog.json_formatter diff --git a/app/console b/app/console index 118fee251..ec53685c1 100755 --- a/app/console +++ b/app/console @@ -17,7 +17,7 @@ $debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('- && $env !== 'build'; if ($debug) { - Debug::enable(); + Debug::enable(~E_USER_DEPRECATED); } $kernel = new AppKernel($env, $debug); diff --git a/app_dev.php.dist b/app_dev.php.dist index 6355b73b8..01590cd3a 100644 --- a/app_dev.php.dist +++ b/app_dev.php.dist @@ -4,7 +4,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Debug\Debug; $loader = require_once __DIR__.'/../app/bootstrap.php.cache'; -Debug::enable(); +Debug::enable(~E_USER_DEPRECATED); require_once __DIR__.'/../app/AppKernel.php'; diff --git a/composer.json b/composer.json index d1a26a281..7366c4451 100644 --- a/composer.json +++ b/composer.json @@ -10,11 +10,11 @@ "minimum-stability": "stable", "require": { "php": ">=5.4", - "symfony/symfony": "~2.6,>=2.6.6", + "symfony/symfony": "^2.7", "twig/extensions": "~1.0", "symfony/assetic-bundle": "~2.3", "symfony/monolog-bundle": "~2.4", - "sensio/distribution-bundle": "~3.0", + "sensio/distribution-bundle": "^3.0.21", "sensio/framework-extra-bundle": "~3.0", "incenteev/composer-parameter-handler": "~2.0", "nelmio/security-bundle": "~1.4", @@ -23,14 +23,15 @@ "fortawesome/font-awesome": "~4.2.0", "jms/translation-bundle": "~1.1.0", "jms/di-extra-bundle": "~1.4.0", - "surfnet/stepup-middleware-client-bundle": "0.3.*", + "surfnet/stepup-middleware-client-bundle": "^1.0", "guzzlehttp/guzzle": "~4", "simplesamlphp/saml2": "dev-master", - "surfnet/stepup-saml-bundle": "0.4.*", - "surfnet/stepup-bundle": "0.2.*", + "surfnet/stepup-saml-bundle": "^1.0", + "surfnet/stepup-bundle": "^1.0", "symfony/swiftmailer-bundle": "~2.3" }, "require-dev": { + "mockery/mockery": "~0.9.0", "sensio/generator-bundle": "~2.3", "ibuildings/qa-tools": "~1.1,>=1.1.27", "liip/rmt": "1.1.*" diff --git a/composer.lock b/composer.lock index 8f019b41b..91763727a 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 http://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#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "0c8f0d3a50f6351252c1337aa87d5440", + "hash": "6eb470b6b29c044302244eb5bd458886", "packages": [ { "name": "beberlei/assert", @@ -1148,12 +1148,12 @@ "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/moontoast/math.git", + "url": "https://github.com/ramsey/moontoast-math.git", "reference": "fce28a9d1e73e73376cb44e5e581675d15fbe2f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/moontoast/math/zipball/fce28a9d1e73e73376cb44e5e581675d15fbe2f3", + "url": "https://api.github.com/repos/ramsey/moontoast-math/zipball/fce28a9d1e73e73376cb44e5e581675d15fbe2f3", "reference": "fce28a9d1e73e73376cb44e5e581675d15fbe2f3", "shasum": "" }, @@ -1190,7 +1190,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phiamo/MopaBootstrapBundle/zipball/818b0f47ebd352559950e9a64431ff9472e8a9dd", + "url": "https://api.github.com/repos/phiamo/MopaBootstrapBundle/zipball/4d4b6291b47fc70491a4f92105be39d6bcccbb95", "reference": "818b0f47ebd352559950e9a64431ff9472e8a9dd", "shasum": "" }, @@ -1426,12 +1426,12 @@ "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", + "url": "https://github.com/ramsey/rhumsaa-uuid.git", "reference": "cca98c652cac412c9c2f109c69e5532f313435fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/cca98c652cac412c9c2f109c69e5532f313435fc", + "url": "https://api.github.com/repos/ramsey/rhumsaa-uuid/zipball/cca98c652cac412c9c2f109c69e5532f313435fc", "reference": "cca98c652cac412c9c2f109c69e5532f313435fc", "shasum": "" }, @@ -1489,29 +1489,36 @@ }, { "name": "sensio/distribution-bundle", - "version": "v3.0.8", + "version": "v3.0.30", "target-dir": "Sensio/Bundle/DistributionBundle", "source": { "type": "git", "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", - "reference": "bc5e96bb4faf6bee7121085951d11b89488952f5" + "reference": "f1758b30096202aeede61f79a1dffd69da091517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/bc5e96bb4faf6bee7121085951d11b89488952f5", - "reference": "bc5e96bb4faf6bee7121085951d11b89488952f5", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/f1758b30096202aeede61f79a1dffd69da091517", + "reference": "f1758b30096202aeede61f79a1dffd69da091517", "shasum": "" }, "require": { "php": ">=5.3.3", "sensiolabs/security-checker": "~2.0", "symfony/class-loader": "~2.2", - "symfony/form": "~2.2", "symfony/framework-bundle": "~2.3", - "symfony/process": "~2.2", + "symfony/process": "~2.2" + }, + "require-dev": { + "symfony/form": "~2.2", "symfony/validator": "~2.2", "symfony/yaml": "~2.2" }, + "suggest": { + "symfony/form": "If you want to use the configurator", + "symfony/validator": "If you want to use the configurator", + "symfony/yaml": "If you want to use the configurator" + }, "type": "symfony-bundle", "extra": { "branch-alias": { @@ -1538,7 +1545,7 @@ "configuration", "distribution" ], - "time": "2014-11-03 21:16:34" + "time": "2015-06-05 22:32:22" }, { "name": "sensio/framework-extra-bundle", @@ -1650,7 +1657,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SURFnet/saml2/zipball/fb22b707c7a4705d2d54401715c8f6d229e88a74", + "url": "https://api.github.com/repos/SURFnet/saml2/zipball/b2d96e911d8e9348edd75bcd980f60a251fa5c28", "reference": "fb22b707c7a4705d2d54401715c8f6d229e88a74", "shasum": "" }, @@ -1750,19 +1757,20 @@ }, { "name": "surfnet/stepup-bundle", - "version": "0.2.0", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/SURFnet/Stepup-bundle.git", - "reference": "57c493202612595d2b5efeccdd7af538292afee7" + "reference": "7e201ac7f5b870a142a85d3a44f1e2ebb8b31933" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SURFnet/Stepup-bundle/zipball/57c493202612595d2b5efeccdd7af538292afee7", - "reference": "57c493202612595d2b5efeccdd7af538292afee7", + "url": "https://api.github.com/repos/SURFnet/Stepup-bundle/zipball/7e201ac7f5b870a142a85d3a44f1e2ebb8b31933", + "reference": "7e201ac7f5b870a142a85d3a44f1e2ebb8b31933", "shasum": "" }, "require": { + "ext-gmp": "*", "ext-openssl": "*", "graylog2/gelf-php": "~1.1", "guzzlehttp/guzzle": "~4", @@ -1771,8 +1779,10 @@ "sensio/framework-extra-bundle": "~3", "symfony/config": "~2", "symfony/dependency-injection": "~2", - "symfony/form": "~2", + "symfony/form": "^2.7", + "symfony/framework-bundle": "~2", "symfony/http-kernel": "~2", + "symfony/twig-bridge": "~2", "symfony/validator": "~2" }, "require-dev": { @@ -1796,20 +1806,20 @@ "suaas", "surfnet" ], - "time": "2015-04-02 14:06:30" + "time": "2015-06-19 11:44:48" }, { "name": "surfnet/stepup-middleware-client-bundle", - "version": "0.3.0", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/SURFnet/Stepup-Middleware-clientbundle.git", - "reference": "2fc784ccccbd87f546f1454df15c8746e3a5d4e5" + "reference": "c4c35c790acb4be964980e64b461c03a034e2066" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SURFnet/Stepup-Middleware-clientbundle/zipball/2fc784ccccbd87f546f1454df15c8746e3a5d4e5", - "reference": "2fc784ccccbd87f546f1454df15c8746e3a5d4e5", + "url": "https://api.github.com/repos/SURFnet/Stepup-Middleware-clientbundle/zipball/c4c35c790acb4be964980e64b461c03a034e2066", + "reference": "c4c35c790acb4be964980e64b461c03a034e2066", "shasum": "" }, "require": { @@ -1845,26 +1855,27 @@ "Apache-2.0" ], "description": "Symfony2 bundle for consuming the Step-up Middleware API.", - "time": "2015-05-04 11:55:44" + "time": "2015-06-19 09:21:37" }, { "name": "surfnet/stepup-saml-bundle", - "version": "0.4.0", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/SURFnet/Stepup-saml-bundle.git", - "reference": "a13e448701de643a821ad3c28892af6646e8ceb9" + "reference": "8f336c442d3dc9d045bff498c4b27de65213565a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SURFnet/Stepup-saml-bundle/zipball/a13e448701de643a821ad3c28892af6646e8ceb9", - "reference": "a13e448701de643a821ad3c28892af6646e8ceb9", + "url": "https://api.github.com/repos/SURFnet/Stepup-saml-bundle/zipball/8f336c442d3dc9d045bff498c4b27de65213565a", + "reference": "8f336c442d3dc9d045bff498c4b27de65213565a", "shasum": "" }, "require": { "ext-openssl": "*", "php": "~5.4", "simplesamlphp/saml2": "dev-master", + "symfony/dependency-injection": "^2.6", "symfony/framework-bundle": "~2.5" }, "require-dev": { @@ -1891,7 +1902,7 @@ "stepup", "surfnet" ], - "time": "2015-05-04 11:45:24" + "time": "2015-06-19 10:11:01" }, { "name": "swiftmailer/swiftmailer", @@ -2128,25 +2139,26 @@ }, { "name": "symfony/symfony", - "version": "v2.6.6", + "version": "v2.7.0", "source": { "type": "git", "url": "https://github.com/symfony/symfony.git", - "reference": "48c9e835a877adfb023b8b6d033d9dd14f342b4b" + "reference": "9975b1eca3de4db792a2c3e4e16f676a4aadcd46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/symfony/zipball/48c9e835a877adfb023b8b6d033d9dd14f342b4b", - "reference": "48c9e835a877adfb023b8b6d033d9dd14f342b4b", + "url": "https://api.github.com/repos/symfony/symfony/zipball/9975b1eca3de4db792a2c3e4e16f676a4aadcd46", + "reference": "9975b1eca3de4db792a2c3e4e16f676a4aadcd46", "shasum": "" }, "require": { "doctrine/common": "~2.3", - "php": ">=5.3.3", + "php": ">=5.3.9", "psr/log": "~1.0", - "twig/twig": "~1.12,>=1.12.3" + "twig/twig": "~1.18" }, "replace": { + "symfony/asset": "self.version", "symfony/browser-kit": "self.version", "symfony/class-loader": "self.version", "symfony/config": "self.version", @@ -2170,7 +2182,6 @@ "symfony/monolog-bridge": "self.version", "symfony/options-resolver": "self.version", "symfony/process": "self.version", - "symfony/propel1-bridge": "self.version", "symfony/property-access": "self.version", "symfony/proxy-manager-bridge": "self.version", "symfony/routing": "self.version", @@ -2201,18 +2212,23 @@ "ircmaxell/password-compat": "~1.0", "monolog/monolog": "~1.11", "ocramius/proxy-manager": "~0.4|~1.0", - "propel/propel1": "~1.6", - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "self.version" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "psr-0": { - "Symfony\\": "src/" + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", + "Symfony\\Bundle\\": "src/Symfony/Bundle/", + "Symfony\\Component\\": "src/Symfony/Component/" }, "classmap": [ "src/Symfony/Component/HttpFoundation/Resources/stubs", @@ -2227,21 +2243,21 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "The Symfony PHP framework", - "homepage": "http://symfony.com", + "homepage": "https://symfony.com", "keywords": [ "framework" ], - "time": "2015-04-01 16:55:26" + "time": "2015-05-30 16:52:28" }, { "name": "twbs/bootstrap", @@ -2348,25 +2364,25 @@ }, { "name": "twig/twig", - "version": "v1.16.2", + "version": "v1.18.1", "source": { "type": "git", - "url": "https://github.com/fabpot/Twig.git", - "reference": "42f758d9fe2146d1f0470604fc05ee43580873fc" + "url": "https://github.com/twigphp/Twig.git", + "reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fabpot/Twig/zipball/42f758d9fe2146d1f0470604fc05ee43580873fc", - "reference": "42f758d9fe2146d1f0470604fc05ee43580873fc", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9f70492f44398e276d1b81c1b43adfe6751c7b7f", + "reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f", "shasum": "" }, "require": { - "php": ">=5.2.4" + "php": ">=5.2.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.16-dev" + "dev-master": "1.18-dev" } }, "autoload": { @@ -2392,7 +2408,7 @@ }, { "name": "Twig Team", - "homepage": "https://github.com/fabpot/Twig/graphs/contributors", + "homepage": "http://twig.sensiolabs.org/contributors", "role": "Contributors" } ], @@ -2401,7 +2417,7 @@ "keywords": [ "templating" ], - "time": "2014-10-17 12:53:44" + "time": "2015-04-19 08:30:27" } ], "packages-dev": [ @@ -2533,12 +2549,12 @@ "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/Behat/Mink.git", + "url": "https://github.com/minkphp/Mink.git", "reference": "090900a0049c441f1e072bbd837db4079b2250c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Mink/zipball/090900a0049c441f1e072bbd837db4079b2250c5", + "url": "https://api.github.com/repos/minkphp/Mink/zipball/090900a0049c441f1e072bbd837db4079b2250c5", "reference": "090900a0049c441f1e072bbd837db4079b2250c5", "shasum": "" }, @@ -2588,12 +2604,12 @@ "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/Behat/MinkBrowserKitDriver.git", + "url": "https://github.com/minkphp/MinkBrowserKitDriver.git", "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/MinkBrowserKitDriver/zipball/aed8f4a596b79014a75254c3e337511c33e38cbd", + "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/aed8f4a596b79014a75254c3e337511c33e38cbd", "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd", "shasum": "" }, @@ -2694,12 +2710,12 @@ "version": "v1.1.0", "source": { "type": "git", - "url": "https://github.com/Behat/MinkGoutteDriver.git", + "url": "https://github.com/minkphp/MinkGoutteDriver.git", "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/MinkGoutteDriver/zipball/2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", + "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", "shasum": "" }, @@ -2746,12 +2762,12 @@ "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/Behat/MinkSelenium2Driver.git", + "url": "https://github.com/minkphp/MinkSelenium2Driver.git", "reference": "8018fee80bf6573f909ece3e0dfc07d0eb352210" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/MinkSelenium2Driver/zipball/8018fee80bf6573f909ece3e0dfc07d0eb352210", + "url": "https://api.github.com/repos/minkphp/MinkSelenium2Driver/zipball/8018fee80bf6573f909ece3e0dfc07d0eb352210", "reference": "8018fee80bf6573f909ece3e0dfc07d0eb352210", "shasum": "" }, @@ -2899,12 +2915,12 @@ "version": "v2.0.2", "source": { "type": "git", - "url": "https://github.com/fabpot/Goutte.git", + "url": "https://github.com/FriendsOfPHP/Goutte.git", "reference": "b12c3f7ec68d8814b50444cfe142fd0a056557f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fabpot/Goutte/zipball/b12c3f7ec68d8814b50444cfe142fd0a056557f9", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/b12c3f7ec68d8814b50444cfe142fd0a056557f9", "reference": "b12c3f7ec68d8814b50444cfe142fd0a056557f9", "shasum": "" }, @@ -2943,6 +2959,51 @@ ], "time": "2014-07-22 13:24:11" }, + { + "name": "hamcrest/hamcrest-php", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "1.3.3", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "hamcrest" + ], + "files": [ + "hamcrest/Hamcrest.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "time": "2015-05-11 14:41:42" + }, { "name": "ibuildings/qa-tools", "version": "1.1.27", @@ -3112,6 +3173,71 @@ ], "time": "2014-10-28 10:33:21" }, + { + "name": "mockery/mockery", + "version": "0.9.4", + "source": { + "type": "git", + "url": "https://github.com/padraic/mockery.git", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "~1.1", + "lib-pcre": ">=7.0", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/padraic/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2015-04-02 19:54:00" + }, { "name": "pdepend/pdepend", "version": "2.0.3", diff --git a/src/Surfnet/StepupSelfService/SamlStepupProviderBundle/DependencyInjection/SurfnetStepupSelfServiceSamlStepupProviderExtension.php b/src/Surfnet/StepupSelfService/SamlStepupProviderBundle/DependencyInjection/SurfnetStepupSelfServiceSamlStepupProviderExtension.php index 461f76c9c..a66bfafef 100644 --- a/src/Surfnet/StepupSelfService/SamlStepupProviderBundle/DependencyInjection/SurfnetStepupSelfServiceSamlStepupProviderExtension.php +++ b/src/Surfnet/StepupSelfService/SamlStepupProviderBundle/DependencyInjection/SurfnetStepupSelfServiceSamlStepupProviderExtension.php @@ -19,12 +19,12 @@ namespace Surfnet\StepupSelfService\SamlStepupProviderBundle\DependencyInjection; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\DependencyInjection\Loader; class SurfnetStepupSelfServiceSamlStepupProviderExtension extends Extension { diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/RevokeCommand.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/RevokeCommand.php index d4cec0f23..aeefddd80 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/RevokeCommand.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/RevokeCommand.php @@ -18,7 +18,6 @@ namespace Surfnet\StepupSelfService\SelfServiceBundle\Command; -use Surfnet\StepupMiddlewareClientBundle\Command\AbstractCommand; use Symfony\Component\Validator\Constraints as Assert; class RevokeCommand diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/SendSmsChallengeCommand.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/SendSmsChallengeCommand.php index 0277f4da9..386272a62 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/SendSmsChallengeCommand.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/SendSmsChallengeCommand.php @@ -18,14 +18,15 @@ namespace Surfnet\StepupSelfService\SelfServiceBundle\Command; +use Surfnet\StepupBundle\Value\PhoneNumber\Country; use Symfony\Component\Validator\Constraints as Assert; class SendSmsChallengeCommand { /** - * @var string + * @var Country */ - public $countryCode; + public $country; /** * @Assert\NotBlank(message="ss.send_sms_challenge_command.recipient.may_not_be_empty") diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/SendSmsCommand.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/SendSmsCommand.php deleted file mode 100644 index 92e95ffde..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Command/SendSmsCommand.php +++ /dev/null @@ -1,59 +0,0 @@ -doSecondFactorsExistForIdentity($identity->id)) { return $this->redirect($this->generateUrl('ss_second_factor_list')); } else { - $this->get('session')->getFlashBag()->add('notice', 'ss.registration.selector.alert.no_second_factors_yet'); - return $this->redirect( $this->generateUrl('ss_registration_display_types') ); diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Controller/LocaleController.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Controller/LocaleController.php new file mode 100644 index 000000000..447e67241 --- /dev/null +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Controller/LocaleController.php @@ -0,0 +1,68 @@ +query->get('return-url'); + + /** @var LoggerInterface $logger */ + $logger = $this->get('logger'); + $logger->info('Switching locale...'); + + $identity = $this->getIdentity(); + if (!$identity) { + throw new AccessDeniedHttpException('Cannot switch locales when not authenticated'); + } + + $command = new SwitchLocaleCommand(); + $command->identityId = $identity->id; + + $form = $this->createForm( + 'stepup_switch_locale', + $command, + ['route' => 'ss_switch_locale', 'route_parameters' => ['return_url' => $returnUrl]] + ); + $form->handleRequest($request); + + if (!$form->isValid()) { + $this->addFlash('error', $this->get('translator')->trans('ss.flash.invalid_switch_locale_form')); + $logger->error('The switch locale form unexpectedly contained invalid data'); + return $this->redirect($returnUrl); + } + + $service = $this->get('self_service.service.identity'); + if (!$service->switchLocale($command)) { + $this->addFlash('error', $this->get('translator')->trans('ss.flash.error_while_switching_locale')); + $logger->error('An error occurred while switching locales'); + return $this->redirect($returnUrl); + } + + $logger->info('Successfully switched locale'); + + return $this->redirect($returnUrl); + } +} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/DateTime/DateTime.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/DateTime/DateTime.php deleted file mode 100644 index a3d48538e..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/DateTime/DateTime.php +++ /dev/null @@ -1,37 +0,0 @@ -root('surfnet_stepup_self_service_self_service'); - - $this->createGatewayApiConfiguration($rootNode); - $this->createSmsConfiguration($rootNode); - - return $treeBuilder; - } - - private function createGatewayApiConfiguration(ArrayNodeDefinition $root) - { - $root - ->children() - ->arrayNode('gateway_api') - ->info('Gateway API configuration') - ->children() - ->arrayNode('credentials') - ->info('Basic authentication credentials') - ->children() - ->scalarNode('username') - ->info('Username for the Gateway API') - ->isRequired() - ->validate() - ->ifTrue(function ($value) { - return (!is_string($value) || empty($value)); - }) - ->thenInvalid( - 'Invalid Gateway API username specified: "%s". Must be non-empty string' - ) - ->end() - ->end() - ->scalarNode('password') - ->info('Password for the Gateway API') - ->isRequired() - ->validate() - ->ifTrue(function ($value) { - return (!is_string($value) || empty($value)); - }) - ->thenInvalid( - 'Invalid Gateway API password specified: "%s". Must be non-empty string' - ) - ->end() - ->end() - ->end() - ->end() - ->scalarNode('url') - ->info('The URL to the Gateway application (e.g. https://gateway.tld)') - ->isRequired() - ->validate() - ->ifTrue(function ($value) { - return (!is_string($value) || empty($value) || !preg_match('~/$~', $value)); - }) - ->thenInvalid( - 'Invalid Gateway URL specified: "%s". Must be string ending in forward slash' - ) - ->end() - ->end() - ->end() - ->end() - ->end(); - } - - private function createSmsConfiguration(ArrayNodeDefinition $root) - { - $root - ->children() - ->arrayNode('sms') - ->info('SMS configuration') - ->isRequired() - ->children() - ->scalarNode('originator') - ->info('Originator (sender) for SMS messages') - ->isRequired() - ->validate() - ->ifTrue(function ($value) { - return (!is_string($value) || !preg_match('~^[a-z0-9]{1,11}$~i', $value)); - }) - ->thenInvalid( - 'Invalid SMS originator specified: "%s". Must be a string matching ' - . '"~^[a-z0-9]{1,11}$~i".' - ) - ->end() - ->end() - ->integerNode('otp_expiry_interval') - ->info('After how many seconds an SMS challenge OTP expires') - ->isRequired() - ->validate() - ->ifTrue(function ($value) { - return $value <= 0; - }) - ->thenInvalid( - 'Invalid SMS challenge OTP expiry, must be one or more seconds.' - ) - ->end() - ->end() - ->integerNode('maximum_otp_requests') - ->info('How many challenges a user may request during a session') - ->isRequired() - ->validate() - ->ifTrue(function ($value) { - return $value <= 0; - }) - ->thenInvalid( - 'Maximum OTP requests has a minimum of 1' - ) - ->end() - ->end() - ->end() - ->end() - ->end(); - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/DependencyInjection/SurfnetStepupSelfServiceSelfServiceExtension.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/DependencyInjection/SurfnetStepupSelfServiceSelfServiceExtension.php index f22f44ff6..37ad19666 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/DependencyInjection/SurfnetStepupSelfServiceSelfServiceExtension.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/DependencyInjection/SurfnetStepupSelfServiceSelfServiceExtension.php @@ -35,36 +35,11 @@ class SurfnetStepupSelfServiceSelfServiceExtension extends Extension */ public function load(array $configs, ContainerBuilder $container) { - $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); - $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); - $gatewayGuzzleOptions = [ - 'base_url' => $config['gateway_api']['url'], - 'defaults' => [ - 'auth' => [ - $config['gateway_api']['credentials']['username'], - $config['gateway_api']['credentials']['password'], - 'basic' - ], - 'headers' => [ - 'Accept' => 'application/json' - ] - ] - ]; - - $gatewayGuzzle = $container->getDefinition('surfnet_stepup_self_service_self_service.guzzle.gateway_api'); - $gatewayGuzzle->replaceArgument(0, $gatewayGuzzleOptions); - - $smsSecondFactorService = - $container->getDefinition('surfnet_stepup_self_service_self_service.service.sms_second_factor'); - $smsSecondFactorService->replaceArgument(4, $config['sms']['originator']); - - $container - ->getDefinition('surfnet_stepup_self_service_self_service.challenge_handler') - ->replaceArgument(2, $config['sms']['otp_expiry_interval']) - ->replaceArgument(3, $config['sms']['maximum_otp_requests']); + $container->getDefinition('self_service.locale.request_stack_locale_provider') + ->replaceArgument(1, $container->getParameter('default_locale')) + ->replaceArgument(2, $container->getParameter('locales')); } } diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/EventListener/LocaleListener.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/EventListener/LocaleListener.php new file mode 100644 index 000000000..9cdfe1cd9 --- /dev/null +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/EventListener/LocaleListener.php @@ -0,0 +1,78 @@ +tokenStorage = $tokenStorage; + $this->translator = $translator; + } + + public function setRequestLocale(GetResponseEvent $event) + { + $token = $this->tokenStorage->getToken(); + + if (!$token) { + return; + } + + /** @var Identity $identity */ + $identity = $token->getUser(); + + $request = $event->getRequest(); + $request->setLocale($identity->preferredLocale); + + // As per \Symfony\Component\HttpKernel\EventListener\TranslatorListener::setLocale() + try { + $this->translator->setLocale($request->getLocale()); + } catch (\InvalidArgumentException $e) { + $this->translator->setLocale($request->getDefaultLocale()); + } + } + + public static function getSubscribedEvents() + { + return [ + // Default locale listener listens at P16 + // Translator listener, which sets the locale for the translator, listens at P10 + // The firewall, which makes the token available, listens at P8 + // We must jump in after the firewall, forcing us to overwrite the translator locale. + KernelEvents::REQUEST => ['setRequestLocale', 7], + ]; + } +} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/AnchorType.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/AnchorType.php index 3869ca0f3..84da346ca 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/AnchorType.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/AnchorType.php @@ -22,7 +22,7 @@ use Symfony\Component\Form\ButtonTypeInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class AnchorType extends AbstractType implements ButtonTypeInterface { @@ -36,7 +36,7 @@ public function getName() return 'anchor'; } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'route' => null, @@ -45,9 +45,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) $resolver->setRequired(['route']); - $resolver->setAllowedTypes([ - 'route' => 'string', - ]); + $resolver->setAllowedTypes('route', 'string'); } public function buildView(FormView $view, FormInterface $form, array $options) diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/ProveYubikeyPossessionType.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/ProveYubikeyPossessionType.php index df9aa1c91..7b5ab1257 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/ProveYubikeyPossessionType.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/ProveYubikeyPossessionType.php @@ -20,7 +20,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class ProveYubikeyPossessionType extends AbstractType { @@ -39,7 +39,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'Surfnet\StepupSelfService\SelfServiceBundle\Command\VerifyYubikeyOtpCommand', diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/RevokeSecondFactorType.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/RevokeSecondFactorType.php index 035d2d992..6994bbd12 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/RevokeSecondFactorType.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/RevokeSecondFactorType.php @@ -20,7 +20,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class RevokeSecondFactorType extends AbstractType { @@ -38,7 +38,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'Surfnet\StepupSelfService\SelfServiceBundle\Command\RevokeCommand', diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/SendSmsChallengeType.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/SendSmsChallengeType.php index 9a456fec6..8c2f6541d 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/SendSmsChallengeType.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/SendSmsChallengeType.php @@ -21,18 +21,20 @@ use Surfnet\StepupBundle\Value\PhoneNumber\CountryCodeListing; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class SendSmsChallengeType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('countryCode', 'choice', [ + ->add('country', 'choice', [ 'label' => /** @Ignore */ 'country code', 'horizontal_label_class' => 'sr-only', 'required' => true, 'choice_list' => CountryCodeListing::asChoiceList(), + 'preferred_choices' => + ['Surfnet\StepupBundle\Value\PhoneNumber\CountryCodeListing', 'isPreferredChoice'], 'horizontal_input_wrapper_class' => 'foo', ]) ->add('subscriber', 'text', [ @@ -51,7 +53,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'attr' => ['class' => 'form-inline'], diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifyEmailType.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifyEmailType.php index 594db6f21..b06d850d2 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifyEmailType.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifyEmailType.php @@ -20,7 +20,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class VerifyEmailType extends AbstractType { @@ -40,7 +40,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'Surfnet\StepupSelfService\SelfServiceBundle\Identity\Command\VerifyEmailCommand', diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifySmsChallengeType.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifySmsChallengeType.php index 605554e1a..3dcd454fe 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifySmsChallengeType.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Form/Type/VerifySmsChallengeType.php @@ -20,7 +20,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class VerifySmsChallengeType extends AbstractType { @@ -44,7 +44,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'Surfnet\StepupSelfService\SelfServiceBundle\Command\VerifySmsChallengeCommand', diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/Exception/TooManyChallengesRequestedException.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Locale/PreferredLocaleProvider.php similarity index 73% rename from src/Surfnet/StepupSelfService/SelfServiceBundle/Service/Exception/TooManyChallengesRequestedException.php rename to src/Surfnet/StepupSelfService/SelfServiceBundle/Locale/PreferredLocaleProvider.php index d7865ce2c..f1bef77b5 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/Exception/TooManyChallengesRequestedException.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Locale/PreferredLocaleProvider.php @@ -16,10 +16,12 @@ * limitations under the License. */ -namespace Surfnet\StepupSelfService\SelfServiceBundle\Service\Exception; +namespace Surfnet\StepupSelfService\SelfServiceBundle\Locale; -use Surfnet\StepupSelfService\SelfServiceBundle\Exception\DomainException; - -class TooManyChallengesRequestedException extends DomainException +interface PreferredLocaleProvider { + /** + * @return string + */ + public function providePreferredLocale(); } diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Locale/RequestStackLocaleProvider.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Locale/RequestStackLocaleProvider.php new file mode 100644 index 000000000..5b17ac7dc --- /dev/null +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Locale/RequestStackLocaleProvider.php @@ -0,0 +1,82 @@ + $supportedLocale) { + if (!is_string($supportedLocale)) { + $parameterName = sprintf('supportedLocales[%s]', $key); + throw InvalidArgumentException::invalidType('string', $parameterName, $supportedLocale); + } + } + + $this->requestStack = $requestStack; + $this->defaultLocale = $defaultLocale; + $this->supportedLocales = $supportedLocales; + } + + public function providePreferredLocale() + { + $preferredLocale = $this->requestStack->getCurrentRequest()->getPreferredLanguage($this->supportedLocales); + + if (!$preferredLocale) { + return $this->defaultLocale; + } + + return $preferredLocale; + } +} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/routing.yml b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/routing.yml index 978bf9502..93eb8ce94 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/routing.yml +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/routing.yml @@ -79,3 +79,10 @@ selfservice_serviceprovider_consume_assertion: path: /authentication/consume-assertion methods: [POST] defaults: { _controller: SurfnetStepupSelfServiceSelfServiceBundle:Saml:consumeAssertion } + +ss_switch_locale: + path: /switch-locale + methods: [POST] + defaults: { _controller: SurfnetStepupSelfServiceSelfServiceBundle:Locale:switchLocale } + requirements: + 'return-url': '.+' diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/services.yml b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/services.yml index fa8539241..cdab0ec47 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/services.yml +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/services.yml @@ -47,22 +47,9 @@ services: public: false class: Surfnet\StepupSelfService\SelfServiceBundle\Service\YubikeyService arguments: - - @surfnet_stepup_self_service_self_service.guzzle.gateway_api + - @surfnet_stepup.guzzle.gateway_api - @logger - surfnet_stepup_self_service_self_service.service.sms: - public: false - class: Surfnet\StepupSelfService\SelfServiceBundle\Service\SmsService - arguments: - - @surfnet_stepup_self_service_self_service.guzzle.gateway_api - - @logger - - surfnet_stepup_self_service_self_service.guzzle.gateway_api: - public: false - class: GuzzleHttp\Client - arguments: - - {} # Set from the extension - surfnet_stepup_self_service_self_service.service.yubikey_second_factor: class: Surfnet\StepupSelfService\SelfServiceBundle\Service\YubikeySecondFactorService arguments: @@ -72,31 +59,21 @@ services: surfnet_stepup_self_service_self_service.service.sms_second_factor: class: Surfnet\StepupSelfService\SelfServiceBundle\Service\SmsSecondFactorService arguments: - - @surfnet_stepup_self_service_self_service.service.sms - - @surfnet_stepup_self_service_self_service.challenge_handler + - @surfnet_stepup.service.sms_second_factor - @translator - @surfnet_stepup_self_service_self_service.service.command - - '' # Originator set in extension surfnet_stepup_self_service_self_service.service.gssf: class: Surfnet\StepupSelfService\SelfServiceBundle\Service\GssfService arguments: - @surfnet_stepup_self_service_self_service.service.command - surfnet_stepup_self_service_self_service.challenge_handler: - public: false - class: Surfnet\StepupSelfService\SelfServiceBundle\Service\SmsSecondFactor\SessionSmsVerificationStateHandler - arguments: - - @session - - 'Surfnet/SelfService/SecondFactor/Sms:challenge' - - {} # OTP expiry interval - - 0 # Maximum OTP requests - self_service.service.identity: class: Surfnet\StepupSelfService\SelfServiceBundle\Service\IdentityService arguments: - @surfnet_stepup_middleware_client.identity.service.identity - @surfnet_stepup_self_service_self_service.service.command + - @security.token_storage - @logger self_service.service.ra: @@ -109,6 +86,7 @@ services: arguments: - @self_service.service.identity - @surfnet_saml.saml.attribute_dictionary + - @self_service.locale.request_stack_locale_provider self_service.security.authentication.listener: class: Surfnet\StepupSelfService\SelfServiceBundle\Security\Firewall\SamlListener @@ -135,3 +113,15 @@ services: class: Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\SessionHandler arguments: - @session + + self_service.event_listener.locale: + class: Surfnet\StepupSelfService\SelfServiceBundle\EventListener\LocaleListener + arguments: [ @security.token_storage, @translator ] + tags: [{ name: kernel.event_subscriber }] + + self_service.locale.request_stack_locale_provider: + class: Surfnet\StepupSelfService\SelfServiceBundle\Locale\RequestStackLocaleProvider + arguments: + - @request_stack + - '' # See extension + - [] # See extension diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/public/less/style.less b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/public/less/style.less index 795ae3f43..8c74d1ea9 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/public/less/style.less +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/public/less/style.less @@ -63,6 +63,13 @@ form[name="ss_verify_email"] { } } +form[name="stepup_switch_locale"] { + .pull-right(); +} +select[name="stepup_switch_locale[locale]"] { + min-width: 150px; +} + .generate-progress(100); .generate-progress(@n, @i: 0) when (@i =< @n) { diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/Registration/Sms/sendChallenge.html.twig b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/Registration/Sms/sendChallenge.html.twig index a4c0626d0..941691332 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/Registration/Sms/sendChallenge.html.twig +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/Registration/Sms/sendChallenge.html.twig @@ -28,7 +28,7 @@
{{ form_errors(form) }}
- {{ form_widget(form.countryCode) }} + {{ form_widget(form.country) }}
{{ form_widget(form.subscriber) }} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/translations.twig b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/translations.twig index de1dc8fd3..7ef9a12e0 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/translations.twig +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/translations.twig @@ -1,6 +1,3 @@ -{# EntryPointController #} -{{ 'ss.registration.selector.alert.no_second_factors_yet'|trans }} - {# src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/views/Registration/partial/secondFactor.html.twig #} {{ 'ss.registration.selector.sms.alt'|trans }} {{ 'ss.registration.selector.sms.title'|trans }} @@ -16,20 +13,20 @@ {{ 'ss.registration.selector.tiqr.button.use'|trans }} {# SmsController form errors #} -{{ 'ss.prove_phone_possession.send_sms_challenge_failed'|trans({}, 'validators') }} -{{ 'ss.prove_phone_possession.proof_of_possession_failed'|trans({}, 'validators') }} -{{ 'ss.prove_phone_possession.incorrect_challenge_response'|trans({}, 'validators') }} -{{ 'ss.prove_phone_possession.challenge_request_limit_reached'|trans({}, 'validators') }} -{{ 'ss.prove_phone_possession.challenge_expired'|trans({}, 'validators') }} -{{ 'ss.prove_phone_possession.too_many_attempts'|trans({}, 'validators') }} +{{ 'ss.prove_phone_possession.send_sms_challenge_failed'|trans }} +{{ 'ss.prove_phone_possession.proof_of_possession_failed'|trans }} +{{ 'ss.prove_phone_possession.incorrect_challenge_response'|trans }} +{{ 'ss.prove_phone_possession.challenge_request_limit_reached'|trans }} +{{ 'ss.prove_phone_possession.challenge_expired'|trans }} +{{ 'ss.prove_phone_possession.too_many_attempts'|trans }} {# SmsController flash messages #} {{ 'ss.registration.sms.alert.no_verification_state'|trans }} {# YubikeyController from errors #} -{{ 'ss.verify_yubikey_command.otp.otp_invalid'|trans({}, 'validators') }} -{{ 'ss.verify_yubikey_command.otp.verification_error'|trans({}, 'validators') }} -{{ 'ss.prove_phone_possession.challenge_response_incorrect'|trans({}, 'validators') }} -{{ 'ss.prove_yubikey_possession.proof_of_possession_failed'|trans({}, 'validators') }} +{{ 'ss.verify_yubikey_command.otp.otp_invalid'|trans }} +{{ 'ss.verify_yubikey_command.otp.verification_error'|trans }} +{{ 'ss.prove_phone_possession.challenge_response_incorrect'|trans }} +{{ 'ss.prove_yubikey_possession.proof_of_possession_failed'|trans }} {# SecondFactorController list #} {{ 'ss.second_factor.list.text.vetted'|trans }} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/Provider/SamlProvider.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/Provider/SamlProvider.php index be01655ab..e1618243b 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/Provider/SamlProvider.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/Provider/SamlProvider.php @@ -21,6 +21,7 @@ use Surfnet\SamlBundle\SAML2\Attribute\AttributeDictionary; use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\Identity; use Surfnet\StepupMiddlewareClientBundle\Uuid\Uuid; +use Surfnet\StepupSelfService\SelfServiceBundle\Locale\PreferredLocaleProvider; use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\Token\SamlToken; use Surfnet\StepupSelfService\SelfServiceBundle\Service\IdentityService; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; @@ -38,12 +39,19 @@ class SamlProvider implements AuthenticationProviderInterface */ private $attributeDictionary; + /** + * @var \Symfony\Component\HttpFoundation\PreferredLocaleProvider + */ + private $preferredLocaleProvider; + public function __construct( IdentityService $identityService, - AttributeDictionary $attributeDictionary + AttributeDictionary $attributeDictionary, + PreferredLocaleProvider $preferredLocaleProvider ) { $this->identityService = $identityService; $this->attributeDictionary = $attributeDictionary; + $this->preferredLocaleProvider = $preferredLocaleProvider; } /** @@ -63,11 +71,12 @@ public function authenticate(TokenInterface $token) if ($identity === null) { $identity = new Identity(); - $identity->id = Uuid::generate(); - $identity->nameId = $nameId; - $identity->institution = $institution; - $identity->email = $email; - $identity->commonName = $commonName; + $identity->id = Uuid::generate(); + $identity->nameId = $nameId; + $identity->institution = $institution; + $identity->email = $email; + $identity->commonName = $commonName; + $identity->preferredLocale = $this->preferredLocaleProvider->providePreferredLocale(); $this->identityService->createIdentity($identity); } elseif ($identity->email !== $email || $identity->commonName !== $commonName) { diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/SessionHandler.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/SessionHandler.php index 98d3bde19..727a24113 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/SessionHandler.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/SessionHandler.php @@ -19,7 +19,6 @@ namespace Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication; use Symfony\Component\HttpFoundation\Session\SessionInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; class SessionHandler { diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/IdentityService.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/IdentityService.php index f04cdde93..0545827ed 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/IdentityService.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/IdentityService.php @@ -20,15 +20,22 @@ use Exception; use Psr\Log\LoggerInterface; +use Surfnet\StepupBundle\Command\SwitchLocaleCommand; use Surfnet\StepupMiddlewareClient\Identity\Dto\IdentitySearchQuery; use Surfnet\StepupMiddlewareClientBundle\Identity\Command\CreateIdentityCommand; +use Surfnet\StepupMiddlewareClientBundle\Identity\Command\ExpressLocalePreferenceCommand; use Surfnet\StepupMiddlewareClientBundle\Identity\Command\UpdateIdentityCommand; use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\Identity; use Surfnet\StepupMiddlewareClientBundle\Identity\Service\IdentityService as ApiIdentityService; use Surfnet\StepupSelfService\SelfServiceBundle\Exception\RuntimeException; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) -- Hard to reduce due to different commands and queries used. + */ class IdentityService implements UserProviderInterface { /** @@ -41,6 +48,11 @@ class IdentityService implements UserProviderInterface */ private $commandService; + /** + * @var \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface + */ + private $tokenStorage; + /** * @var \Psr\Log\LoggerInterface */ @@ -49,10 +61,12 @@ class IdentityService implements UserProviderInterface public function __construct( ApiIdentityService $apiIdentityService, CommandService $commandService, + TokenStorageInterface $tokenStorage, LoggerInterface $logger ) { $this->apiIdentityService = $apiIdentityService; $this->commandService = $commandService; + $this->tokenStorage = $tokenStorage; $this->logger = $logger; } @@ -128,11 +142,12 @@ public function findByNameIdAndInstitution($nameId, $institution) public function createIdentity(Identity $identity) { $command = new CreateIdentityCommand(); - $command->id = $identity->id; - $command->nameId = $identity->nameId; - $command->institution = $identity->institution; - $command->email = $identity->email; - $command->commonName = $identity->commonName; + $command->id = $identity->id; + $command->nameId = $identity->nameId; + $command->institution = $identity->institution; + $command->email = $identity->email; + $command->commonName = $identity->commonName; + $command->preferredLocale = $identity->preferredLocale; $this->processCommand($command); } @@ -142,13 +157,43 @@ public function createIdentity(Identity $identity) */ public function updateIdentity(Identity $identity) { - $command = new UpdateIdentityCommand($identity->id); + $command = new UpdateIdentityCommand($identity->id, $identity->institution); $command->email = $identity->email; $command->commonName = $identity->commonName; $this->processCommand($command); } + + /** + * @param SwitchLocaleCommand $command + * @return bool + */ + public function switchLocale(SwitchLocaleCommand $command) + { + /** @var TokenInterface|null */ + $token = $this->tokenStorage->getToken(); + + if (!$token) { + throw new RuntimeException('Cannot switch locales when unauthenticated'); + } + + /** @var Identity $identity */ + $identity = $token->getUser(); + + $expressLocalePreferenceCommand = new ExpressLocalePreferenceCommand(); + $expressLocalePreferenceCommand->identityId = $command->identityId; + $expressLocalePreferenceCommand->preferredLocale = $command->locale; + + $result = $this->commandService->execute($expressLocalePreferenceCommand); + + if ($result->isSuccessful()) { + $identity->preferredLocale = $command->locale; + } + + return $result->isSuccessful(); + } + /** * @param $command */ diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/Otp.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/Otp.php deleted file mode 100644 index 1d2193216..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/Otp.php +++ /dev/null @@ -1,105 +0,0 @@ -otp = $otpString; - $otp->phoneNumber = $phoneNumber; - $otp->expiryInterval = $expiryInterval; - $otp->issuedAt = DateTime::now(); - - return $otp; - } - - private function __construct() - { - } - - public function verify($userOtp) - { - if (!is_string($userOtp)) { - throw InvalidArgumentException::invalidType('string', 'userOtp', $userOtp); - } - - if (strtoupper($userOtp) !== strtoupper($this->otp)) { - return OtpVerification::noMatch(); - } - - $expiryTime = clone $this->issuedAt; - $expiryTime->add($this->expiryInterval); - - if ($expiryTime <= DateTime::now()) { - return OtpVerification::matchExpired(); - } - - return OtpVerification::foundMatch($this->phoneNumber); - } - - /** - * @param string $phoneNumber - * @return bool - */ - public function hasPhoneNumber($phoneNumber) - { - return $this->phoneNumber === $phoneNumber; - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/OtpVerification.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/OtpVerification.php deleted file mode 100644 index e10ca2427..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/OtpVerification.php +++ /dev/null @@ -1,106 +0,0 @@ -status = $status; - $this->phoneNumber = $phoneNumber; - } - - /** - * @return bool - */ - public function wasSuccessful() - { - return $this->status === self::STATUS_FOUND_MATCH; - } - - /** - * @return bool - */ - public function didOtpMatch() - { - return $this->status === self::STATUS_FOUND_MATCH || $this->status === self::STATUS_MATCH_EXPIRED; - } - /** - * @return bool - */ - public function didOtpExpire() - { - return $this->status === self::STATUS_MATCH_EXPIRED; - } - - /** - * @return bool - */ - public function wasAttemptedTooManyTimes() - { - return $this->status === self::STATUS_TOO_MANY_ATTEMPTS; - } - - /** - * @return null|string Only guaranteed to be a string when status is successful. - */ - public function getPhoneNumber() - { - return $this->phoneNumber; - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SessionSmsVerificationStateHandler.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SessionSmsVerificationStateHandler.php deleted file mode 100644 index 227687f62..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SessionSmsVerificationStateHandler.php +++ /dev/null @@ -1,117 +0,0 @@ -session = $session; - $this->sessionKey = $sessionKey; - $this->otpExpiryInterval = new DateInterval(sprintf('PT%dS', $otpExpiryInterval)); - $this->otpRequestMaximum = $otpRequestMaximum; - } - - public function hasState() - { - return $this->session->has($this->sessionKey); - } - - public function clearState() - { - $this->session->remove($this->sessionKey); - } - - public function requestNewOtp($phoneNumber) - { - /** @var SmsVerificationState|null $state */ - $state = $this->session->get($this->sessionKey); - - if (!$state) { - $state = new SmsVerificationState($this->otpExpiryInterval, $this->otpRequestMaximum); - $this->session->set($this->sessionKey, $state); - } - - return $state->requestNewOtp($phoneNumber); - } - - public function getOtpRequestsRemainingCount() - { - /** @var SmsVerificationState|null $state */ - $state = $this->session->get($this->sessionKey); - - return $state ? $state->getOtpRequestsRemainingCount() : $this->otpRequestMaximum; - } - - public function getMaximumOtpRequestsCount() - { - return $this->otpRequestMaximum; - } - - public function verify($otp) - { - /** @var SmsVerificationState|null $state */ - $state = $this->session->get($this->sessionKey); - - if (!$state) { - return OtpVerification::matchExpired(); - } - - $verification = $state->verify($otp); - - if ($verification->wasSuccessful()) { - $this->session->remove($this->sessionKey); - } - - return $verification; - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SmsVerificationState.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SmsVerificationState.php deleted file mode 100644 index 9f9d36440..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SmsVerificationState.php +++ /dev/null @@ -1,133 +0,0 @@ -expiryInterval = $expiryInterval; - $this->maximumOtpRequests= $maximumOtpRequests; - $this->otps = []; - $this->verificationAttemptsMade = 0; - } - - /** - * @param string $phoneNumber - * @return string The generated OTP string. - */ - public function requestNewOtp($phoneNumber) - { - if (!is_string($phoneNumber) || empty($phoneNumber)) { - throw InvalidArgumentException::invalidType('string', 'phoneNumber', $phoneNumber); - } - - if (count($this->otps) >= $this->maximumOtpRequests) { - throw new TooManyChallengesRequestedException( - sprintf( - '%d OTPs were requested, while only %d requests are allowed', - count($this->otps) + 1, - $this->maximumOtpRequests - ) - ); - } - - $this->otps = array_filter($this->otps, function (Otp $otp) use ($phoneNumber) { - return $otp->hasPhoneNumber($phoneNumber); - }); - - $otp = OtpGenerator::generate(8); - $this->otps[] = Otp::create($otp, $phoneNumber, $this->expiryInterval); - - return $otp; - } - - /** - * @param string $userOtp - * @return OtpVerification - */ - public function verify($userOtp) - { - if ($this->verificationAttemptsMade >= self::MAXIMUM_VERIFICATION_ATTEMPTS) { - return OtpVerification::tooManyAttempts(); - } - - $this->verificationAttemptsMade++; - - if (!is_string($userOtp)) { - throw InvalidArgumentException::invalidType('string', 'userOtp', $userOtp); - } - - foreach ($this->otps as $otp) { - $verification = $otp->verify($userOtp); - - if ($verification->didOtpMatch()) { - return $verification; - } - } - - return OtpVerification::noMatch(); - } - - /** - * @return int - */ - public function getOtpRequestsRemainingCount() - { - return $this->maximumOtpRequests - count($this->otps); - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SmsVerificationStateHandler.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SmsVerificationStateHandler.php deleted file mode 100644 index fb199485d..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsSecondFactor/SmsVerificationStateHandler.php +++ /dev/null @@ -1,62 +0,0 @@ -smsService = $smsService; - $this->smsVerificationStateHandler = $smsVerificationStateHandler; + $this->smsSecondFactorService = $smsSecondFactorService; $this->translator = $translator; $this->commandService = $commandService; - $this->originator = $originator; } /** @@ -98,7 +72,7 @@ public function __construct( */ public function getOtpRequestsRemainingCount() { - return $this->smsVerificationStateHandler->getOtpRequestsRemainingCount(); + return $this->smsSecondFactorService->getOtpRequestsRemainingCount(); } /** @@ -106,7 +80,7 @@ public function getOtpRequestsRemainingCount() */ public function getMaximumOtpRequestsCount() { - return $this->smsVerificationStateHandler->getMaximumOtpRequestsCount(); + return $this->smsSecondFactorService->getMaximumOtpRequestsCount(); } /** @@ -114,12 +88,12 @@ public function getMaximumOtpRequestsCount() */ public function hasSmsVerificationState() { - return $this->smsVerificationStateHandler->hasState(); + return $this->smsSecondFactorService->hasSmsVerificationState(); } public function clearSmsVerificationState() { - $this->smsVerificationStateHandler->clearState(); + $this->smsSecondFactorService->clearSmsVerificationState(); } /** @@ -130,21 +104,17 @@ public function clearSmsVerificationState() public function sendChallenge(SendSmsChallengeCommand $command) { $phoneNumber = new InternationalPhoneNumber( - new CountryCode($command->countryCode), + $command->country->getCountryCode(), new PhoneNumber($command->subscriber) ); - $otp = $this->smsVerificationStateHandler->requestNewOtp((string) $phoneNumber); - - $body = $this->translator->trans('ss.registration.sms.challenge_body', ['%challenge%' => $otp]); - $smsCommand = new SendSmsCommand(); - $smsCommand->recipient = $phoneNumber->toMSISDN(); - $smsCommand->originator = $this->originator; - $smsCommand->body = $body; - $smsCommand->identity = $command->identity; - $smsCommand->institution = $command->institution; + $stepupCommand = new StepupSendSmsChallengeCommand(); + $stepupCommand->phoneNumber = $phoneNumber; + $stepupCommand->body = $this->translator->trans('ss.registration.sms.challenge_body'); + $stepupCommand->identity = $command->identity; + $stepupCommand->institution = $command->institution; - return $this->smsService->sendSms($smsCommand); + return $this->smsSecondFactorService->sendChallenge($stepupCommand); } /** @@ -153,7 +123,10 @@ public function sendChallenge(SendSmsChallengeCommand $command) */ public function provePossession(VerifySmsChallengeCommand $challengeCommand) { - $verification = $this->smsVerificationStateHandler->verify($challengeCommand->challenge); + $stepupCommand = new VerifyPossessionOfPhoneCommand(); + $stepupCommand->challenge = $challengeCommand->challenge; + + $verification = $this->smsSecondFactorService->verifyPossession($stepupCommand); if ($verification->didOtpExpire()) { return ProofOfPossessionResult::challengeExpired(); diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsService.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsService.php deleted file mode 100644 index a64ad10ed..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/SmsService.php +++ /dev/null @@ -1,97 +0,0 @@ -guzzleClient = $guzzleClient; - $this->logger = $logger; - } - - /** - * @param SendSmsCommand $command - * @return bool - */ - public function sendSms(SendSmsCommand $command) - { - $this->logger->info('Sending SMS'); - - $body = [ - 'requester' => ['institution' => $command->institution, 'identity' => $command->identity], - 'message' => [ - 'originator' => $command->originator, - 'recipient' => $command->recipient, - 'body' => $command->body - ], - ]; - $response = $this->guzzleClient->post('api/send-sms', ['json' => $body, 'exceptions' => false]); - $statusCode = $response->getStatusCode(); - - if ($statusCode != 200) { - $this->logger->error( - sprintf('SMS sending failed, error: [%s] %s', $response->getStatusCode(), $response->getReasonPhrase()), - ['http-body' => $response->getBody() ? $response->getBody()->getContents() : '',] - ); - - return false; - } - - try { - $result = $response->json(); - } catch (\RuntimeException $e) { - $this->logger->error('SMS sending failed; server responded with malformed JSON.'); - - return false; - } - - if (!isset($result['status'])) { - $this->logger->error('SMS sending failed; server responded without status report.'); - - return false; - } - - if ($result['status'] !== 'OK') { - $this->logger->error('SMS sending failed; server responded with non-OK status report.'); - - return false; - } - - return true; - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/YubikeySecondFactorService.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/YubikeySecondFactorService.php index 0730e2d93..3dc4a824c 100644 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/YubikeySecondFactorService.php +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Service/YubikeySecondFactorService.php @@ -18,6 +18,8 @@ namespace Surfnet\StepupSelfService\SelfServiceBundle\Service; +use Surfnet\StepupBundle\Value\YubikeyOtp; +use Surfnet\StepupBundle\Value\YubikeyPublicId; use Surfnet\StepupMiddlewareClientBundle\Identity\Command\ProveYubikeyPossessionCommand; use Surfnet\StepupMiddlewareClientBundle\Uuid\Uuid; use Surfnet\StepupSelfService\SelfServiceBundle\Command\VerifyYubikeyOtpCommand; @@ -68,10 +70,13 @@ public function provePossession(VerifyYubikeyOtpCommand $command) $secondFactorId = Uuid::generate(); + $otp = YubikeyOtp::fromString($command->otp); + $publicId = YubikeyPublicId::fromOtp($otp); + $provePossessionCommand = new ProveYubikeyPossessionCommand(); $provePossessionCommand->identityId = $command->identity; $provePossessionCommand->secondFactorId = $secondFactorId; - $provePossessionCommand->yubikeyPublicId = substr($command->otp, 0, 12); + $provePossessionCommand->yubikeyPublicId = $publicId->getYubikeyPublicId(); $result = $this->commandService->execute($provePossessionCommand); diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/DateTimeHelper.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/DateTimeHelper.php deleted file mode 100644 index 1d3828b1a..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/DateTimeHelper.php +++ /dev/null @@ -1,37 +0,0 @@ -setAccessible(true); - $nowProperty->setValue($now); - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Locale/RequestStackLocaleProviderTest.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Locale/RequestStackLocaleProviderTest.php new file mode 100644 index 000000000..399bf597a --- /dev/null +++ b/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Locale/RequestStackLocaleProviderTest.php @@ -0,0 +1,96 @@ +shouldReceive('getPreferredLanguage')->with(['en_GB', 'nl_NL'])->once()->andReturn('nl_NL'); + + $requestStack = m::mock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStack->shouldReceive('getCurrentRequest')->with()->once()->andReturn($request); + + $provider = new RequestStackLocaleProvider($requestStack, 'en_GB', ['en_GB', 'nl_NL']); + $this->assertEquals('nl_NL', $provider->providePreferredLocale()); + } + + /** + * @test + */ + public function it_falls_back_to_the_default_locale() + { + $request = m::mock('Symfony\Component\HttpFoundation\Request'); + $request->shouldReceive('getPreferredLanguage')->with(['en_GB', 'nl_NL'])->once()->andReturn(null); + + $requestStack = m::mock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStack->shouldReceive('getCurrentRequest')->with()->once()->andReturn($request); + + $provider = new RequestStackLocaleProvider($requestStack, 'de_DE', ['en_GB', 'nl_NL']); + $this->assertEquals('de_DE', $provider->providePreferredLocale()); + } + + public function non_strings() + { + return [ + 'array' => [array()], + 'integer' => [1], + 'object' => [new \stdClass()], + 'null' => [null], + 'bool' => [false], + 'resource' => [fopen('php://memory', 'w')], + ]; + } + + /** + * @test + * @dataProvider non_strings + * @expectedException Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException + * @expectedExceptionMessage given for "defaultLocale" + * @param mixed $nonString + */ + public function it_requires_the_default_locale_to_be_a_string($nonString) + { + $requestStack = m::mock('Symfony\Component\HttpFoundation\RequestStack'); + + new RequestStackLocaleProvider($requestStack, $nonString, ['en_GB', 'nl_NL']); + } + + /** + * @test + * @dataProvider non_strings + * @expectedException Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException + * @expectedExceptionMessage given for "supportedLocales[1]" + * @param mixed $nonString + */ + public function it_requires_the_supported_locales_to_be_strings($nonString) + { + $requestStack = m::mock('Symfony\Component\HttpFoundation\RequestStack'); + + new RequestStackLocaleProvider($requestStack, 'nl_NL', ['en_GB', $nonString]); + } +} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Service/SmsSecondFactor/OtpTest.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Service/SmsSecondFactor/OtpTest.php deleted file mode 100644 index 950a9d9ee..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Service/SmsSecondFactor/OtpTest.php +++ /dev/null @@ -1,106 +0,0 @@ - [array()], - 'integer' => [1], - 'object' => [new \stdClass()], - 'null' => [null], - 'bool' => [false], - 'resource' => [fopen('php://memory', 'w')], - ]; - } - - public function non_non_empty_strings() - { - return [ - 'empty string' => [''], - 'array' => [array()], - 'integer' => [1], - 'object' => [new \stdClass()], - 'null' => [null], - 'bool' => [false], - 'resource' => [fopen('php://memory', 'w')], - ]; - } - - /** - * @test - * @group sms - */ - public function can_be_created() - { - Otp::create('ABCDEFG', '123', new DateInterval('PT5M')); - } - - /** - * @test - * @group sms - * @dataProvider non_non_empty_strings - * @param mixed $nonString - */ - public function only_accepts_string_otps($nonString) - { - $this->setExpectedException( - 'Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException', - 'otpString' - ); - Otp::create($nonString, '123', new DateInterval('PT5M')); - } - - /** - * @test - * @group sms - * @dataProvider non_non_empty_strings - * @param mixed $nonString - */ - public function only_accepts_string_phone_numbers($nonString) - { - $this->setExpectedException( - 'Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException', - 'phoneNumber' - ); - Otp::create('ABCDEFG', $nonString, new DateInterval('PT5M')); - } - - /** - * @test - * @group sms - * @dataProvider non_strings - * @param mixed $nonString - */ - public function it_verifies_only_string_otps($nonString) - { - $this->setExpectedException( - 'Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException', - 'otpString' - ); - $otp = Otp::create($nonString, '123', new DateInterval('PT5M')); - $otp->verify($nonString); - } -} diff --git a/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Service/SmsSecondFactor/SmsVerificationStateTest.php b/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Service/SmsSecondFactor/SmsVerificationStateTest.php deleted file mode 100644 index bdb0d58ac..000000000 --- a/src/Surfnet/StepupSelfService/SelfServiceBundle/Tests/Service/SmsSecondFactor/SmsVerificationStateTest.php +++ /dev/null @@ -1,288 +0,0 @@ - [array()], - 'integer' => [1], - 'object' => [new \stdClass()], - 'null' => [null], - 'bool' => [false], - 'resource' => [fopen('php://memory', 'w')], - ]; - } - - public function non_non_empty_strings() - { - return [ - 'empty string' => [''], - 'array' => [array()], - 'integer' => [1], - 'object' => [new \stdClass()], - 'null' => [null], - 'bool' => [false], - 'resource' => [fopen('php://memory', 'w')], - ]; - } - - /** - * @test - * @group sms - */ - public function it_can_be_matched() - { - $state = new SmsVerificationState(new DateInterval('PT15M'), 3); - $otp = $state->requestNewOtp('123'); - - $this->assertTrue($state->verify($otp)->wasSuccessful(), 'OTP should have matched'); - } - - /** - * @test - * @group sms - * @dataProvider non_non_empty_strings - * @param mixed $nonString - */ - public function it_accepts_only_string_phone_numbers($nonString) - { - $this->setExpectedException( - 'Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException', - 'phoneNumber' - ); - $state = new SmsVerificationState(new DateInterval('PT15M'), 3); - $state->requestNewOtp($nonString); - } - - /** - * @test - * @group sms - * @dataProvider non_strings - * @param mixed $nonString - */ - public function it_verifies_only_string_otps($nonString) - { - $this->setExpectedException( - 'Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException', - 'userOtp' - ); - $state = new SmsVerificationState(new DateInterval('PT15M'), 3); - $state->requestNewOtp('123'); - $state->verify($nonString); - } - - /** - * @test - * @group sms - */ - public function it_can_expire() - { - DateTimeHelper::setCurrentTime(new DateTime('@0')); - $state = new SmsVerificationState(new DateInterval('PT1S'), 3); - $otp = $state->requestNewOtp('123'); - - DateTimeHelper::setCurrentTime(new DateTime('@1')); - $verification = $state->verify($otp); - - $this->assertFalse($verification->wasSuccessful(), "Verification shouldn't be successful"); - $this->assertTrue($verification->didOtpExpire(), 'OTP should have expired'); - $this->assertTrue($verification->didOtpMatch(), 'OTP should have matched'); - } - - /** - * @test - * @group sms - */ - public function the_expiration_time_is_pushed_back_with_each_new_otp() - { - // Set a challenge - DateTimeHelper::setCurrentTime(new DateTime('@0')); - $state = new SmsVerificationState(new DateInterval('PT5S'), 3); - $otp = $state->requestNewOtp('123'); - - // Try after 3 seconds - DateTimeHelper::setCurrentTime(new DateTime('@3')); - $this->assertTrue($state->verify($otp)->wasSuccessful(), "OTP should've matched"); - - // Set a new challenge - $otp = $state->requestNewOtp('123'); - - // Try after 4 seconds (total of 7 seconds, longer than 5-second expiry interval) - DateTimeHelper::setCurrentTime(new DateTime('@7')); - $this->assertTrue($state->verify($otp)->wasSuccessful(), "OTP should've matched"); - } - - /** - * @test - * @group sms - */ - public function the_consumer_can_request_too_many_otps_but_can_keep_track_of_remaining_requests() - { - $state = new SmsVerificationState(new DateInterval('PT10S'), 3); - $this->assertSame(3, $state->getOtpRequestsRemainingCount()); - - $state->requestNewOtp('123'); - $this->assertSame(2, $state->getOtpRequestsRemainingCount()); - - $state->requestNewOtp('123'); - $this->assertSame(1, $state->getOtpRequestsRemainingCount()); - - $state->requestNewOtp('123'); - $this->assertSame(0, $state->getOtpRequestsRemainingCount()); - $this->assertSame(0, $state->getOtpRequestsRemainingCount()); - - $this->setExpectedException( - 'Surfnet\StepupSelfService\SelfServiceBundle\Service\Exception\TooManyChallengesRequestedException' - ); - $state->requestNewOtp('123'); - $this->assertSame(0, $state->getOtpRequestsRemainingCount()); - } - - public function lteZeroMaximumTries() - { - return [[0], [-1], [-1000]]; - } - - /** - * @test - * @group sms - * @dataProvider lteZeroMaximumTries - * @param int $maximumTries - */ - public function maximum_challenges_must_be_gte_1($maximumTries) - { - $this->setExpectedException( - 'Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException', - 'maximum OTP requests' - ); - - new SmsVerificationState(new DateInterval('PT15M'), $maximumTries); - } - - /** - * @test - * @group sms - */ - public function a_previous_otp_can_be_matched() - { - DateTimeHelper::setCurrentTime(new DateTime('@0')); - $state = new SmsVerificationState(new DateInterval('PT5S'), 3); - $otp1 = $state->requestNewOtp('123'); - $otp2 = $state->requestNewOtp('123'); - - $this->assertTrue($state->verify($otp1)->wasSuccessful(), "OTP should've matched"); - $this->assertTrue($state->verify($otp2)->wasSuccessful(), "OTP should've matched"); - } - - /** - * @test - * @group sms - */ - public function otp_matching_is_case_insensitive() - { - DateTimeHelper::setCurrentTime(new DateTime('@0')); - $state = new SmsVerificationState(new DateInterval('PT5S'), 3); - $otp = $state->requestNewOtp('123'); - - $this->assertTrue($state->verify(strtolower($otp))->wasSuccessful(), "OTP should've matched"); - $this->assertTrue($state->verify(strtoupper($otp))->wasSuccessful(), "OTP should've matched"); - } - - /** - * @test - * @group sms - */ - public function no_more_than_10_attempts_can_be_made_overall() - { - $state = new SmsVerificationState(new DateInterval('PT5S'), 3); - $state->requestNewOtp('237894'); - - for ($i = 0; $i < SmsVerificationState::MAXIMUM_VERIFICATION_ATTEMPTS; $i++) { - $this->assertFalse($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts not yet achieved'); - } - - $this->assertTrue($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts achieved'); - $this->assertTrue($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts achieved'); - } - - /** - * @test - * @group sms - */ - public function no_more_than_10_attempts_can_be_made_overall_even_when_multiple_otps_requested() - { - $state = new SmsVerificationState(new DateInterval('PT5S'), 99999); - $state->requestNewOtp('237894'); - - for ($i = 0; $i < SmsVerificationState::MAXIMUM_VERIFICATION_ATTEMPTS; $i++) { - $this->assertFalse($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts not yet achieved'); - $state->requestNewOtp('38942'); - } - - $this->assertTrue($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts achieved'); - $this->assertTrue($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts achieved'); - } - - /** - * @test - * @group sms - */ - public function no_more_than_10_attempts_can_be_made_overall_even_when_no_otp_requested() - { - $state = new SmsVerificationState(new DateInterval('PT5S'), 3); - - for ($i = 0; $i < SmsVerificationState::MAXIMUM_VERIFICATION_ATTEMPTS; $i++) { - $this->assertFalse($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts not yet achieved'); - } - - $this->assertTrue($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts achieved'); - $this->assertTrue($state->verify('3')->wasAttemptedTooManyTimes(), 'Failed to assert maximum attempts achieved'); - } - - /** - * @test - * @group sms - */ - public function requesting_an_otp_with_a_different_phone_number_clears_otps_for_other_phone_numbers() - { - $state = new SmsVerificationState(new DateInterval('PT5S'), 3); - - $otpForPhone1 = $state->requestNewOtp('1'); - $otpForPhone2 = $state->requestNewOtp('2'); - - $verificationForPhone1 = $state->verify($otpForPhone1); - $this->assertFalse($verificationForPhone1->wasSuccessful(), 'Verification for phone 1 should not be successful'); - - $verificationForPhone2 = $state->verify($otpForPhone2); - $this->assertTrue($verificationForPhone2->wasSuccessful(), 'Verification for phone 2 should be successful'); - $this->assertSame('2', $verificationForPhone2->getPhoneNumber(), 'Verification for phone 2 should return phone 2'); - } -}