From 6ec227fb0b7ec473518984405fd10a9a14677977 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Tue, 16 Jul 2019 14:51:44 -0500 Subject: [PATCH 1/7] MQE-1600: MFTF Vault integration --- composer.json | 4 +- composer.lock | 603 +++++++++++++++--- etc/config/.credentials.example | 150 ++--- etc/config/.env.example | 4 + .../Handlers/CredentialStore.php | 146 ++--- .../Handlers/SecretStorage/BaseStorage.php | 84 +++ .../Handlers/SecretStorage/FileStorage.php | 119 ++++ .../Handlers/SecretStorage/VaultStorage.php | 131 ++++ .../Test/Util/ActionMergeUtil.php | 2 +- .../Util/TestGenerator.php | 2 +- 10 files changed, 1002 insertions(+), 243 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php diff --git a/composer.json b/composer.json index 8af401526..ddfafee8c 100755 --- a/composer.json +++ b/composer.json @@ -10,10 +10,12 @@ }, "require": { "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", - "allure-framework/allure-codeception": "~1.3.0", "ext-curl": "*", + "allure-framework/allure-codeception": "~1.3.0", "codeception/codeception": "~2.3.4 || ~2.4.0 ", "consolidation/robo": "^1.0.0", + "csharpru/vault-php": "^3.6", + "csharpru/vault-php-guzzle6-transport": "^2.0", "flow/jsonpath": ">0.2", "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", diff --git a/composer.lock b/composer.lock index ca4e287fc..767326d11 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d5879be29cb6bbe70ce7e4a42828303a", + "content-hash": "c6cf409cdb5004e30eff742b235a857a", "packages": [ { "name": "allure-framework/allure-codeception", @@ -168,6 +168,99 @@ ], "time": "2016-10-30T11:50:56+00:00" }, + { + "name": "cache/cache", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-cache/cache.git", + "reference": "902b2e5b54ea57e3a801437748652228c4c58604" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-cache/cache/zipball/902b2e5b54ea57e3a801437748652228c4c58604", + "reference": "902b2e5b54ea57e3a801437748652228c4c58604", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.3", + "league/flysystem": "^1.0", + "php": "^5.6 || ^7.0", + "psr/cache": "^1.0", + "psr/log": "^1.0", + "psr/simple-cache": "^1.0" + }, + "conflict": { + "cache/adapter-common": "*", + "cache/apc-adapter": "*", + "cache/apcu-adapter": "*", + "cache/array-adapter": "*", + "cache/chain-adapter": "*", + "cache/doctrine-adapter": "*", + "cache/filesystem-adapter": "*", + "cache/hierarchical-cache": "*", + "cache/illuminate-adapter": "*", + "cache/memcache-adapter": "*", + "cache/memcached-adapter": "*", + "cache/mongodb-adapter": "*", + "cache/predis-adapter": "*", + "cache/psr-6-doctrine-bridge": "*", + "cache/redis-adapter": "*", + "cache/session-handler": "*", + "cache/taggable-cache": "*", + "cache/void-adapter": "*" + }, + "require-dev": { + "cache/integration-tests": "^0.16", + "defuse/php-encryption": "^2.0", + "illuminate/cache": "^5.4", + "mockery/mockery": "^0.9", + "phpunit/phpunit": "^4.0 || ^5.1", + "predis/predis": "^1.0", + "symfony/cache": "dev-master" + }, + "suggest": { + "ext-apc": "APC extension is required to use the APC Adapter", + "ext-apcu": "APCu extension is required to use the APCu Adapter", + "ext-memcache": "Memcache extension is required to use the Memcache Adapter", + "ext-memcached": "Memcached extension is required to use the Memcached Adapter", + "ext-mongodb": "Mongodb extension required to use the Mongodb adapter", + "ext-redis": "Redis extension is required to use the Redis adapter", + "mongodb/mongodb": "Mongodb lib required to use the Mongodb adapter" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cache\\": "src/" + }, + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Scherer", + "email": "aequasi@gmail.com", + "homepage": "https://github.com/aequasi" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/nyholm" + } + ], + "description": "Library of all the php-cache adapters", + "homepage": "http://www.php-cache.com/en/latest/", + "keywords": [ + "cache", + "psr6" + ], + "time": "2017-03-28T16:08:48+00:00" + }, { "name": "codeception/codeception", "version": "2.3.9", @@ -668,6 +761,92 @@ "homepage": "https://github.com/container-interop/container-interop", "time": "2017-02-14T19:40:03+00:00" }, + { + "name": "csharpru/vault-php", + "version": "3.6.0", + "source": { + "type": "git", + "url": "https://github.com/CSharpRU/vault-php.git", + "reference": "7a7376fe92ee33fe8ca15e728a6bbff1859bd789" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CSharpRU/vault-php/zipball/7a7376fe92ee33fe8ca15e728a6bbff1859bd789", + "reference": "7a7376fe92ee33fe8ca15e728a6bbff1859bd789", + "shasum": "" + }, + "require": { + "cache/cache": "^0.4.0", + "doctrine/inflector": "~1.1", + "guzzlehttp/promises": "^1.3", + "guzzlehttp/psr7": "^1.4", + "psr/cache": "^1.0", + "psr/log": "^1.0", + "weew/helpers-array": "^1.3" + }, + "require-dev": { + "codacy/coverage": "^1.1", + "codeception/codeception": "^2.2", + "csharpru/vault-php-guzzle6-transport": "~2.0", + "php-vcr/php-vcr": "^1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Vault\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Yaroslav Lukyanov", + "email": "c_sharp@mail.ru" + } + ], + "description": "Best Vault client for PHP that you can find", + "time": "2018-05-21T07:05:32+00:00" + }, + { + "name": "csharpru/vault-php-guzzle6-transport", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/CSharpRU/vault-php-guzzle6-transport.git", + "reference": "33c392120ac9f253b62b034e0e8ffbbdb3513bd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CSharpRU/vault-php-guzzle6-transport/zipball/33c392120ac9f253b62b034e0e8ffbbdb3513bd8", + "reference": "33c392120ac9f253b62b034e0e8ffbbdb3513bd8", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "~6.2", + "guzzlehttp/promises": "^1.3", + "guzzlehttp/psr7": "^1.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "VaultTransports\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Yaroslav Lukyanov", + "email": "c_sharp@mail.ru" + } + ], + "description": "Guzzle6 transport for Vault PHP client", + "time": "2019-03-10T06:17:37+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v1.1.0", @@ -795,6 +974,143 @@ ], "time": "2017-02-24T16:22:25+00:00" }, + { + "name": "doctrine/cache", + "version": "v1.6.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b", + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2017-07-22T12:49:21+00:00" + }, + { + "name": "doctrine/inflector", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", + "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2018-01-09T20:05:19+00:00" + }, { "name": "doctrine/instantiator", "version": "1.0.5", @@ -1608,6 +1924,90 @@ ], "time": "2017-05-10T09:20:27+00:00" }, + { + "name": "league/flysystem", + "version": "1.0.53", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "08e12b7628f035600634a5e76d95b5eb66cea674" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/08e12b7628f035600634a5e76d95b5eb66cea674", + "reference": "08e12b7628f035600634a5e76d95b5eb66cea674", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7.10" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2019-06-18T20:09:29+00:00" + }, { "name": "monolog/monolog", "version": "1.24.0", @@ -2679,6 +3079,52 @@ "abandoned": true, "time": "2018-08-09T05:50:03+00:00" }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "time": "2016-08-06T20:24:11+00:00" + }, { "name": "psr/container", "version": "1.0.0", @@ -2825,6 +3271,54 @@ ], "time": "2016-10-10T12:19:37+00:00" }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-10-23T01:57:42+00:00" + }, { "name": "ramsey/uuid", "version": "3.8.0", @@ -4397,6 +4891,43 @@ "validate" ], "time": "2018-01-29T19:49:41+00:00" + }, + { + "name": "weew/helpers-array", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/weew/helpers-array.git", + "reference": "9bff63111f9765b4277750db8d276d92b3e16ed0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/weew/helpers-array/zipball/9bff63111f9765b4277750db8d276d92b3e16ed0", + "reference": "9bff63111f9765b4277750db8d276d92b3e16ed0", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "^4.7", + "satooshi/php-coveralls": "^0.6.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/array.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maxim Kott", + "email": "maximkott@gmail.com" + } + ], + "description": "Useful collection of php array helpers.", + "time": "2016-07-21T11:18:01+00:00" } ], "packages-dev": [ @@ -4556,76 +5087,6 @@ "description": "Experimental Mocking Framework powered by Aspects", "time": "2018-10-07T16:21:11+00:00" }, - { - "name": "doctrine/cache", - "version": "v1.6.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b", - "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b", - "shasum": "" - }, - "require": { - "php": "~5.5|~7.0" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" - }, - "require-dev": { - "phpunit/phpunit": "~4.8|~5.0", - "predis/predis": "~1.0", - "satooshi/php-coveralls": "~0.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "cache", - "caching" - ], - "time": "2017-07-22T12:49:21+00:00" - }, { "name": "gitonomy/gitlib", "version": "v1.0.4", diff --git a/etc/config/.credentials.example b/etc/config/.credentials.example index ea8b03480..d9c73ac66 100644 --- a/etc/config/.credentials.example +++ b/etc/config/.credentials.example @@ -1,75 +1,75 @@ -#carriers/fedex/account= -#carriers/fedex/meter_number= -#carriers/fedex/key= -#carriers/fedex/password= - -#carriers/ups/password= -#carriers/ups/username= -#carriers/ups/access_license_number= -#carriers/ups/shipper_number= - -#carriers/usps/userid= -#carriers/usps/password= - -#carriers_dhl_id_us= -#carriers_dhl_password_us= -#carriers_dhl_account_us= - -#carriers_dhl_id_eu= -#carriers_dhl_password_eu= -#carriers_dhl_account_eu= - - -#payment_authorizenet_login= -#payment_authorizenet_trans_key= -#payment_authorizenet_trans_md5= - -#authorizenet_fraud_review_login= -#authorizenet_fraud_review_trans_key= -#authorizenet_fraud_review_md5= - -#braintree_enabled_fraud_merchant_account_id= -#braintree_enabled_fraud_merchant_id= -#braintree_enabled_fraud_public_key= -#braintree_enabled_fraud_private_key= - -#braintree_disabled_fraud_merchant_account_id= -#braintree_disabled_fraud_merchant_id= -#braintree_disabled_fraud_public_key= -#braintree_disabled_fraud_private_key= - -#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/business_account= -#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/api_username= -#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/api_password= -#payment/paypal_group_all_in_one/wpp_usuk/wpp_required_settings/wpp_and_express_checkout/api_signature= -#payment/paypal_express/merchant_id= - -#payflow_pro_fraud_protection_enabled_business_account= -#payflow_pro_fraud_protection_enabled_partner= -#payflow_pro_fraud_protection_enabled_user= -#payflow_pro_fraud_protection_enabled_pwd= -#payflow_pro_fraud_protection_enabled_vendor= - -#payflow_pro_business_account= -#payflow_pro_partner= -#payflow_pro_user= -#payflow_pro_pwd= -#payflow_pro_vendor= - -#payflow_link_business_account_email= -#payflow_link_partner= -#payflow_link_user= -#payflow_link_password= -#payflow_link_vendor= - -#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/business_account= -#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/api_username= -#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/api_password= -#payment/paypal_group_all_in_one/payments_pro_hosted_solution_with_express_checkout/pphs_required_settings/pphs_required_settings_pphs/api_signature= - -#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/business_account= -#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_username= -#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_password= -#payment/paypal_alternative_payment_methods/express_checkout_us/express_checkout_required/express_checkout_required_express_checkout/api_signature= - -#fraud_protection/signifyd/api_key= \ No newline at end of file +#magento/magento/carriers_fedex_account= +#magento/carriers_fedex_meter_number= +#magento/carriers_fedex_key= +#magento/carriers_fedex_password= + +#magento/carriers_ups_password= +#magento/carriers_ups_username= +#magento/carriers_ups_access_license_number= +#magento/carriers_ups_shipper_number= + +#magento/carriers_usps_userid= +#magento/carriers_usps_password= + +#magento/carriers_dhl_id_us= +#magento/carriers_dhl_password_us= +#magento/carriers_dhl_account_us= + +#magento/carriers_dhl_id_eu= +#magento/carriers_dhl_password_eu= +#magento/carriers_dhl_account_eu= + + +#magento/payment_authorizenet_login= +#magento/payment_authorizenet_trans_key= +#magento/payment_authorizenet_trans_md5= + +#magento/authorizenet_fraud_review_login= +#magento/authorizenet_fraud_review_trans_key= +#magento/authorizenet_fraud_review_md5= + +#magento/braintree_enabled_fraud_merchant_account_id= +#magento/braintree_enabled_fraud_merchant_id= +#magento/braintree_enabled_fraud_public_key= +#magento/braintree_enabled_fraud_private_key= + +#magento/braintree_disabled_fraud_merchant_account_id= +#magento/braintree_disabled_fraud_merchant_id= +#magento/braintree_disabled_fraud_public_key= +#magento/braintree_disabled_fraud_private_key= + +#magento/payment_paypal_group_all_in_one_wpp_usuk_wpp_required_settings_wpp_and_express_checkout_business_account= +#magento/payment_paypal_group_all_in_one_wpp_usuk_wpp_required_settings_wpp_and_express_checkout_api_username= +#magento/payment_paypal_group_all_in_one_wpp_usuk_wpp_required_settings_wpp_and_express_checkout_api_password= +#magento/payment_paypal_group_all_in_one_wpp_usuk_wpp_required_settings_wpp_and_express_checkout_api_signature= +#magento/payment_paypal_express_merchant_id= + +#magento/payflow_pro_fraud_protection_enabled_business_account= +#magento/payflow_pro_fraud_protection_enabled_partner= +#magento/payflow_pro_fraud_protection_enabled_user= +#magento/payflow_pro_fraud_protection_enabled_pwd= +#magento/payflow_pro_fraud_protection_enabled_vendor= + +#magento/payflow_pro_business_account= +#magento/payflow_pro_partner= +#magento/payflow_pro_user= +#magento/payflow_pro_pwd= +#magento/payflow_pro_vendor= + +#magento/payflow_link_business_account_email= +#magento/payflow_link_partner= +#magento/payflow_link_user= +#magento/payflow_link_password= +#magento/payflow_link_vendor= + +#magento/payment_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_required_settings_pphs_business_account= +#magento/payment_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_required_settings_pphs_api_username= +#magento/payment_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_required_settings_pphs_api_password= +#magento/payment_paypal_group_all_in_one_payments_pro_hosted_solution_with_express_checkout_pphs_required_settings_pphs_required_settings_pphs_api_signature= + +#magento/payment_paypal_alternative_payment_methods_express_checkout_us_express_checkout_required_express_checkout_required_express_checkout_business_account= +#magento/payment_paypal_alternative_payment_methods_express_checkout_us_express_checkout_required_express_checkout_required_express_checkout_api_username= +#magento/payment_paypal_alternative_payment_methods_express_checkout_us_express_checkout_required_express_checkout_required_express_checkout_api_password= +#magento/payment_paypal_alternative_payment_methods_express_checkout_us_express_checkout_required_express_checkout_required_express_checkout_api_signature= + +#magento/fraud_protection_signifyd_api_key= \ No newline at end of file diff --git a/etc/config/.env.example b/etc/config/.env.example index 5dc7168be..a220498d9 100644 --- a/etc/config/.env.example +++ b/etc/config/.env.example @@ -30,6 +30,10 @@ BROWSER=chrome #MAGENTO_RESTAPI_SERVER_PORT=8080 #MAGENTO_RESTAPI_SERVER_PROTOCOL=https +*** Uncomment and set vault base url and access token if you want to use vault to manage _CREDS secrets ***# +#CREDENTIAL_VAULT_BASE_URL= +#CREDENTIAL_VAULT_TOKEN= + #*** Uncomment these properties to set up a dev environment with symlinked projects ***# #TESTS_BP= #FW_BP= diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php index a83540683..fd5158a7c 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -6,15 +6,12 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Handlers; -use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; -use Magento\FunctionalTestingFramework\Console\BuildProjectCommand; use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; -use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\SecretStorage\FileStorage; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\SecretStorage\VaultStorage; class CredentialStore { - const ENCRYPTION_ALGO = "AES-256-CBC"; - /** * Singleton instance * @@ -23,25 +20,18 @@ class CredentialStore private static $INSTANCE = null; /** - * Initial vector for open_ssl encryption. - * - * @var string - */ - private $iv = null; - - /** - * Key for open_ssl encryption/decryption + * File storage for credentials * - * @var string + * @var FileStorage */ - private $encodedKey = null; + private $credFile = null; /** - * Key/Value paris of credential names and their corresponding values + * Vault storage for credentials * - * @var array + * @var VaultStorage */ - private $credentials = []; + private $credVault = null; /** * Static singleton getter for CredentialStore Instance @@ -62,116 +52,84 @@ public static function getInstance() */ private function __construct() { - $this->encodedKey = base64_encode(openssl_random_pseudo_bytes(16)); - $this->iv = substr(hash('sha256', $this->encodedKey), 0, 16); - $creds = $this->readInCredentialsFile(); - $this->credentials = $this->encryptCredFileContents($creds); - } - - /** - * Returns the value of a secret based on corresponding key - * - * @param string $key - * @return string|null - * @throws TestFrameworkException - */ - public function getSecret($key) - { - if (!array_key_exists($key, $this->credentials)) { - throw new TestFrameworkException( - "{$key} not defined in .credentials, please provide a value in order to use this secret in a test." - ); + // Initialize vault storage + $csBaseUrl = getenv('CREDENTIAL_VAULT_BASE_URL'); + $csToken = getenv('CREDENTIAL_VAULT_TOKEN'); + if ($csBaseUrl !== false && $csToken !== false) { + try { + $this->credVault = new VaultStorage(rtrim($csBaseUrl, '/'), $csToken); + } catch (TestFrameworkException $e) { + } } - // log here for verbose config - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - LoggingUtil::getInstance()->getLogger(CredentialStore::class)->debug( - "retrieving secret for key name {$key}" - ); + // Initialize file storage + try { + $this->credFile = new FileStorage(); + } catch (TestFrameworkException $e) { } - - return $this->credentials[$key] ?? null; } /** - * Private function which reads in secret key/values from .credentials file and stores in memory as key/value pair. + * Get encrypted value by key * - * @return array + * @param string $key + * @return string|null * @throws TestFrameworkException */ - private function readInCredentialsFile() - { - $credsFilePath = str_replace( - '.credentials.example', - '.credentials', - BuildProjectCommand::CREDENTIALS_FILE_PATH - ); - - if (!file_exists($credsFilePath)) { - throw new TestFrameworkException( - "Cannot find .credentials file, please create in " - . TESTS_BP . " in order to reference sensitive information" - ); - } - - return file($credsFilePath, FILE_IGNORE_NEW_LINES); - } - - /** - * Function which takes the contents of the credentials file and encrypts the entries. - * - * @param array $credContents - * @return array - */ - private function encryptCredFileContents($credContents) + public function getSecret($key) { - $encryptedCreds = []; - foreach ($credContents as $credValue) { - if (substr($credValue, 0, 1) === '#' || empty($credValue)) { - continue; + // Get secret data from vault storage first + if (!is_null($this->credVault)) { + $value = $this->credVault->getEncryptedValue($key); + if (!empty($value)) { + return $value; } + } - list($key, $value) = explode("=", $credValue, 2); + // Get secret data from file when not found in vault + if (!is_null($this->credFile)) { + $value = $this->credFile->getEncryptedValue($key); if (!empty($value)) { - $encryptedCreds[$key] = openssl_encrypt( - $value, - self::ENCRYPTION_ALGO, - $this->encodedKey, - 0, - $this->iv - ); + return $value; } } - return $encryptedCreds; + throw new TestFrameworkException( + "value for key \"$key\" not found in credential storage." + ); } /** - * Takes a value encrypted at runtime and descrypts using the object's initial vector. + * Return decrypted input value * * @param string $value * @return string */ public function decryptSecretValue($value) { - return openssl_decrypt($value, self::ENCRYPTION_ALGO, $this->encodedKey, 0, $this->iv); + if (!is_null($this->credVault)) { + return $this->credVault->getDecryptedValue($value); + } + + if (!is_null($this->credFile)) { + return $this->credFile->getDecryptedValue($value); + } } /** - * Takes a string that contains encrypted data at runtime and decrypts each value. + * Return decrypted values for all occurrences from input string * * @param string $string * @return mixed */ public function decryptAllSecretsInString($string) { - $newString = $string; - foreach ($this->credentials as $name => $secretValue) { - if (strpos($newString, $secretValue) !== false) { - $decryptedValue = $this->decryptSecretValue($secretValue); - $newString = str_replace($secretValue, $decryptedValue, $newString); - } + if (!is_null($this->credVault)) { + return $this->credVault->getAllDecryptedValues($string); + } + + if (!is_null($this->credFile)) { + return $this->credFile->getAllDecryptedValues($string); } - return $newString; } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php new file mode 100644 index 000000000..95ab48d51 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php @@ -0,0 +1,84 @@ + $secretValue) { + if (strpos($newString, $secretValue) !== false) { + $decryptedValue = self::getDecryptedValue($secretValue); + $newString = str_replace($secretValue, $decryptedValue, $newString); + } + } + return $newString; + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php new file mode 100644 index 000000000..cbff33813 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php @@ -0,0 +1,119 @@ +readInCredentialsFile(); + $this->secretData = $this->encryptCredFileContents($creds); + } + + /** + * Returns the value of a secret based on corresponding key + * + * @param string $key + * @return string|null + */ + public function getEncryptedValue($key) + { + // Check if secret is in cached array + if (!is_null($value = parent::getEncryptedValue($key))) { + return $value; + } + + try { + // log here for verbose config + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(FileStorage::class)->debug( + "retrieving secret for key name {$key} from file" + ); + } + + } catch (\Exception $e) { + } + + // Retrieve from file storage + if (!array_key_exists($key, $this->secretData) || empty($value = $this->secretData[$key])) { + return null; + } + + parent::$cachedSecretData[$key] = $value; + return $value; + } + + /** + * Private function which reads in secret key/values from .credentials file and stores in memory as key/value pair + * + * @return array + * @throws TestFrameworkException + */ + private function readInCredentialsFile() + { + $credsFilePath = str_replace( + '.credentials.example', + '.credentials', + BuildProjectCommand::CREDENTIALS_FILE_PATH + ); + + if (!file_exists($credsFilePath)) { + throw new TestFrameworkException( + "Cannot find .credentials file, please create in " + . TESTS_BP . " in order to reference sensitive information" + ); + } + + return file($credsFilePath, FILE_IGNORE_NEW_LINES); + } + + /** + * Function which takes the contents of the credentials file and encrypts the entries + * + * @param array $credContents + * @return array + */ + private function encryptCredFileContents($credContents) + { + $encryptedCreds = []; + foreach ($credContents as $credValue) { + if (substr($credValue, 0, 1) === '#' || empty($credValue)) { + continue; + } + + list($key, $value) = explode("=", $credValue, 2); + if (!empty($value)) { + $encryptedCreds[$key] = openssl_encrypt( + $value, + parent::ENCRYPTION_ALGO, + parent::$encodedKey, + 0, + parent::$iv + ); + } + } + return $encryptedCreds; + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php new file mode 100644 index 000000000..72a67e3e3 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php @@ -0,0 +1,131 @@ + $baseUrl . self::BASE_PATH])); + } + $this->token = $token; + if (!$this->authenticated()) { + throw new TestFrameworkException("Credential Vault: Cannot Authenticate"); + } + } + + /** + * Returns the value of a secret based on corresponding key + * + * @param string $key + * @return string|null + */ + public function getEncryptedValue($key) + { + // Check if secret is in cached array + if (!is_null($value = parent::getEncryptedValue($key))) { + return $value; + } + + try { + // Log here for verbose config + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(VaultStorage::class)->debug( + "retrieving secret for key name {$key} from vault" + ); + } + } catch (\Exception $e) { + } + + // Retrieve from vault storage + if (!$this->authenticated()) { + return null; + } + + // Read value by key from vault + list($vendor, $key) = explode('/', trim($key, '/'), 2); + $url = self::BASE_PATH + . (empty(self::KV_DATA) ? '' : self::KV_DATA) + . self::MFTF_PATH + . '/' + . $vendor + . '/' + . $key; + $value = self::$client->read($url)->getData()['data'][$key]; + + if (empty($value)) { + return null; + } + $eValue = openssl_encrypt($value, parent::ENCRYPTION_ALGO, parent::$encodedKey, 0, parent::$iv); + parent::$cachedSecretData[$key] = $eValue; + return $eValue; + } + + /** + * Check if vault token is still valid. + * + * @return boolean + */ + private function authenticated() + { + try { + // Authenticating using token auth backend. + $authenticated = self::$client + ->setAuthenticationStrategy(new TokenAuthenticationStrategy($this->token)) + ->authenticate(); + + if ($authenticated) { + return true; + } + } catch (\Psr\Cache\InvalidArgumentException $e) { + } + return false; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 2a939a8c7..73a461148 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -149,7 +149,7 @@ private function actionAttributeContainsSecretRef($actionAttributes) return $this->actionAttributeContainsSecretRef($actionAttribute); } - preg_match_all("/{{_CREDS\.([\w]+)}}/", $actionAttribute, $matches); + preg_match_all("/{{_CREDS\.([\w|\/]+)}}/", $actionAttribute, $matches); if (!empty($matches[0])) { return true; diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index d07c5e1e0..a43fedd5a 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -1901,7 +1901,7 @@ private function resolveAllRuntimeReferences($args) { $runtimeReferenceRegex = [ "/{{_ENV\.([\w]+)}}/" => 'getenv', - "/{{_CREDS\.([\w]+)}}/" => 'CredentialStore::getInstance()->getSecret' + "/{{_CREDS\.([\w|\/]+)}}/" => 'CredentialStore::getInstance()->getSecret' ]; $argResult = $args; From 3889f9a8a08a831ab9f1742e28182e31d78d054e Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Wed, 17 Jul 2019 09:19:56 -0500 Subject: [PATCH 2/7] MQE-1600: unit and static test fixes --- .travis.yml | 1 - .../FileStorageTest.php} | 17 +++++++++-------- etc/config/.env.example | 2 +- .../DataGenerator/Handlers/CredentialStore.php | 12 ++++++------ .../Handlers/SecretStorage/BaseStorage.php | 5 ++++- .../Handlers/SecretStorage/FileStorage.php | 3 +-- .../Handlers/SecretStorage/VaultStorage.php | 16 ++++++++-------- 7 files changed, 29 insertions(+), 27 deletions(-) rename dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/{CredentialStoreTest.php => SecretStorage/FileStorageTest.php} (59%) diff --git a/.travis.yml b/.travis.yml index 913c74f48..f807d7415 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: php php: - - 7.0 - 7.1 - 7.2 - 7.3 diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/SecretStorage/FileStorageTest.php similarity index 59% rename from dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php rename to dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/SecretStorage/FileStorageTest.php index a451f8dc9..7e5824c8e 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/CredentialStoreTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/DataGenerator/Handlers/SecretStorage/FileStorageTest.php @@ -4,33 +4,34 @@ * See COPYING.txt for license details. */ -namespace tests\unit\Magento\FunctionalTestFramework\DataGenerator\Handlers; +namespace tests\unit\Magento\FunctionalTestFramework\DataGenerator\Handlers\SecretStorage; -use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore; +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\SecretStorage\FileStorage; use Magento\FunctionalTestingFramework\Util\MagentoTestCase; use AspectMock\Test as AspectMock; -class CredentialStoreTest extends MagentoTestCase +class FileStorageTest extends MagentoTestCase { /** - * Test basic encryption/decryption functionality in CredentialStore class. + * Test basic encryption/decryption functionality in FileStorage class. */ public function testBasicEncryptDecrypt() { - $testKey = 'myKey'; + $testKey = 'magento/myKey'; $testValue = 'myValue'; - AspectMock::double(CredentialStore::class, [ + AspectMock::double(FileStorage::class, [ 'readInCredentialsFile' => ["$testKey=$testValue"] ]); - $encryptedCred = CredentialStore::getInstance()->getSecret($testKey); + $fileStorage = new FileStorage(); + $encryptedCred = $fileStorage->getEncryptedValue($testKey); // assert the value we've gotten is in fact not identical to our test value $this->assertNotEquals($testValue, $encryptedCred); - $actualValue = CredentialStore::getInstance()->decryptSecretValue($encryptedCred); + $actualValue = $fileStorage->getDecryptedValue($encryptedCred); // assert that we are able to successfully decrypt our secret value $this->assertEquals($testValue, $actualValue); diff --git a/etc/config/.env.example b/etc/config/.env.example index a220498d9..cc82ae447 100644 --- a/etc/config/.env.example +++ b/etc/config/.env.example @@ -30,7 +30,7 @@ BROWSER=chrome #MAGENTO_RESTAPI_SERVER_PORT=8080 #MAGENTO_RESTAPI_SERVER_PROTOCOL=https -*** Uncomment and set vault base url and access token if you want to use vault to manage _CREDS secrets ***# +#*** Uncomment and set vault base url and access token if you want to use vault to manage _CREDS secrets ***# #CREDENTIAL_VAULT_BASE_URL= #CREDENTIAL_VAULT_TOKEN= diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php index fd5158a7c..b8bf9876d 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -79,7 +79,7 @@ private function __construct() public function getSecret($key) { // Get secret data from vault storage first - if (!is_null($this->credVault)) { + if (null !== $this->credVault) { $value = $this->credVault->getEncryptedValue($key); if (!empty($value)) { return $value; @@ -87,7 +87,7 @@ public function getSecret($key) } // Get secret data from file when not found in vault - if (!is_null($this->credFile)) { + if (null !== $this->credFile) { $value = $this->credFile->getEncryptedValue($key); if (!empty($value)) { return $value; @@ -107,11 +107,11 @@ public function getSecret($key) */ public function decryptSecretValue($value) { - if (!is_null($this->credVault)) { + if (null !== $this->credVault) { return $this->credVault->getDecryptedValue($value); } - if (!is_null($this->credFile)) { + if (null !== $this->credFile) { return $this->credFile->getDecryptedValue($value); } } @@ -124,11 +124,11 @@ public function decryptSecretValue($value) */ public function decryptAllSecretsInString($string) { - if (!is_null($this->credVault)) { + if (null !== $this->credVault) { return $this->credVault->getAllDecryptedValues($string); } - if (!is_null($this->credFile)) { + if (null !== $this->credFile) { return $this->credFile->getAllDecryptedValues($string); } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php index 95ab48d51..22430e5b6 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php @@ -31,9 +31,12 @@ abstract class BaseStorage */ protected static $cachedSecretData = []; + /** + * BaseStorage constructor + */ public function __construct() { - if (is_null(self::$encodedKey)) { + if (null === self::$encodedKey) { self::$encodedKey = base64_encode(openssl_random_pseudo_bytes(16)); self::$iv = substr(hash('sha256', self::$encodedKey), 0, 16); } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php index cbff33813..1e0ee73d8 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php @@ -41,7 +41,7 @@ public function __construct() public function getEncryptedValue($key) { // Check if secret is in cached array - if (!is_null($value = parent::getEncryptedValue($key))) { + if (null !== ($value = parent::getEncryptedValue($key))) { return $value; } @@ -52,7 +52,6 @@ public function getEncryptedValue($key) "retrieving secret for key name {$key} from file" ); } - } catch (\Exception $e) { } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php index 72a67e3e3..ab23f2642 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php @@ -19,13 +19,13 @@ class VaultStorage extends BaseStorage /** * Adobe Vault */ - //const BASE_PATH = '/dx_magento_qe'; - //const KV_DATA = '/data'; + const BASE_PATH = '/dx_magento_qe'; + const KV_DATA = '/data'; /** * Local Vault */ - const BASE_PATH = '/secret'; - const KV_DATA = '/data'; + //const BASE_PATH = '/secret'; + //const KV_DATA = '/data'; /** * Vault client @@ -51,9 +51,9 @@ class VaultStorage extends BaseStorage public function __construct($baseUrl, $token) { parent::__construct(); - if (is_null(self::$client)) { + if (null === self::$client) { // Creating the client using Guzzle6 Transport and passing a custom url - self::$client = new Client(new Guzzle6Transport(['base_url' => $baseUrl . self::BASE_PATH])); + self::$client = new Client(new Guzzle6Transport(['base_uri' => $baseUrl])); } $this->token = $token; if (!$this->authenticated()) { @@ -70,7 +70,7 @@ public function __construct($baseUrl, $token) public function getEncryptedValue($key) { // Check if secret is in cached array - if (!is_null($value = parent::getEncryptedValue($key))) { + if (null !== ($value = parent::getEncryptedValue($key))) { return $value; } @@ -124,7 +124,7 @@ private function authenticated() if ($authenticated) { return true; } - } catch (\Psr\Cache\InvalidArgumentException $e) { + } catch (\Exception $e) { } return false; } From c3f67ea3fb5c21f6d53484561a42bc010f31dcc9 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Fri, 19 Jul 2019 13:46:12 -0500 Subject: [PATCH 3/7] MQE-1600: MQE-1600: MFTF Vault integration - prefer credentials from local file over vault - address review comments --- .../Handlers/CredentialStore.php | 88 +++++++++++-------- .../Handlers/SecretStorage/FileStorage.php | 25 +++--- .../Handlers/SecretStorage/VaultStorage.php | 71 +++++++-------- .../Test/Util/ActionMergeUtil.php | 3 +- .../Util/TestGenerator.php | 3 +- 5 files changed, 96 insertions(+), 94 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php index b8bf9876d..c122788cb 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -12,31 +12,35 @@ class CredentialStore { + const ARRAY_KEY_FOR_VAULT = 'vault'; + const ARRAY_KEY_FOR_FILE = 'file'; + /** - * Singleton instance + * Numeric indexed array that defines the access precedence of credential storage * - * @var CredentialStore + * @var array */ - private static $INSTANCE = null; + private static $credStoragePrecedence = [self::ARRAY_KEY_FOR_FILE, self::ARRAY_KEY_FOR_VAULT]; /** - * File storage for credentials + * Credential storage array * - * @var FileStorage + * @var array */ - private $credFile = null; + private $credStorage = []; /** - * Vault storage for credentials + * Singleton instance * - * @var VaultStorage + * @var CredentialStore */ - private $credVault = null; + private static $INSTANCE = null; /** * Static singleton getter for CredentialStore Instance * * @return CredentialStore + * @throws TestFrameworkException */ public static function getInstance() { @@ -48,7 +52,9 @@ public static function getInstance() } /** - * CredentialStore constructor. + * CredentialStore constructor + * + * @throws TestFrameworkException */ private function __construct() { @@ -57,16 +63,28 @@ private function __construct() $csToken = getenv('CREDENTIAL_VAULT_TOKEN'); if ($csBaseUrl !== false && $csToken !== false) { try { - $this->credVault = new VaultStorage(rtrim($csBaseUrl, '/'), $csToken); + $this->credStorage[self::ARRAY_KEY_FOR_VAULT] = new VaultStorage( + rtrim($csBaseUrl, '/'), + $csToken + ); } catch (TestFrameworkException $e) { } } // Initialize file storage try { - $this->credFile = new FileStorage(); + $this->credStorage[self::ARRAY_KEY_FOR_FILE] = new FileStorage(); } catch (TestFrameworkException $e) { } + + foreach ($this->credStorage as $cred) { + if (null !== $cred) { + return; + } + } + throw new TestFrameworkException( + "No credential storage is properly configured. Please configure vault or .credentials file." + ); } /** @@ -78,24 +96,20 @@ private function __construct() */ public function getSecret($key) { - // Get secret data from vault storage first - if (null !== $this->credVault) { - $value = $this->credVault->getEncryptedValue($key); - if (!empty($value)) { - return $value; - } - } - - // Get secret data from file when not found in vault - if (null !== $this->credFile) { - $value = $this->credFile->getEncryptedValue($key); - if (!empty($value)) { - return $value; + // Get secret data from storage according to defined precedence + // File storage is preferred over vault storage to allow local secret value overriding remote secret value + foreach (self::$credStoragePrecedence as $credType) { + if (null !== $this->credStorage[$credType]) { + $value = $this->credStorage[$credType]->getEncryptedValue($key); + if (null !== $value) { + return $value; + } } } throw new TestFrameworkException( - "value for key \"$key\" not found in credential storage." + "{$key} not defined in vault or .credentials file, " + . "please provide a value in order to use this secret in a test.\"." ); } @@ -107,12 +121,11 @@ public function getSecret($key) */ public function decryptSecretValue($value) { - if (null !== $this->credVault) { - return $this->credVault->getDecryptedValue($value); - } - - if (null !== $this->credFile) { - return $this->credFile->getDecryptedValue($value); + // Loop through storage to decrypt value + foreach (self::$credStoragePrecedence as $credType) { + if (null !== $this->credStorage[$credType]) { + return $this->credStorage[$credType]->getDecryptedValue($value); + } } } @@ -124,12 +137,11 @@ public function decryptSecretValue($value) */ public function decryptAllSecretsInString($string) { - if (null !== $this->credVault) { - return $this->credVault->getAllDecryptedValues($string); - } - - if (null !== $this->credFile) { - return $this->credFile->getAllDecryptedValues($string); + // Loop through storage to decrypt all occurrences from input string + foreach (self::$credStoragePrecedence as $credType) { + if (null !== $this->credStorage[$credType]) { + return $this->credStorage[$credType]->getAllDecryptedValues($string); + } } } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php index 1e0ee73d8..064610c79 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/FileStorage.php @@ -6,9 +6,9 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Handlers\SecretStorage; -use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Console\BuildProjectCommand; use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; +use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; class FileStorage extends BaseStorage @@ -22,7 +22,6 @@ class FileStorage extends BaseStorage /** * FileStorage constructor - * * @throws TestFrameworkException */ public function __construct() @@ -40,27 +39,24 @@ public function __construct() */ public function getEncryptedValue($key) { + $value = null; // Check if secret is in cached array if (null !== ($value = parent::getEncryptedValue($key))) { return $value; } - try { - // log here for verbose config - if (MftfApplicationConfig::getConfig()->verboseEnabled()) { - LoggingUtil::getInstance()->getLogger(FileStorage::class)->debug( - "retrieving secret for key name {$key} from file" - ); - } - } catch (\Exception $e) { + // log here for verbose config + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(FileStorage::class)->debug( + "retrieving secret for key name {$key} from file" + ); } // Retrieve from file storage - if (!array_key_exists($key, $this->secretData) || empty($value = $this->secretData[$key])) { - return null; + if (array_key_exists($key, $this->secretData) && (null !== ($value = $this->secretData[$key]))) { + parent::$cachedSecretData[$key] = $value; } - parent::$cachedSecretData[$key] = $value; return $value; } @@ -80,8 +76,7 @@ private function readInCredentialsFile() if (!file_exists($credsFilePath)) { throw new TestFrameworkException( - "Cannot find .credentials file, please create in " - . TESTS_BP . " in order to reference sensitive information" + "Credential file is not used: .credentials file not found in " . TESTS_BP ); } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php index ab23f2642..316875b31 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php @@ -20,19 +20,14 @@ class VaultStorage extends BaseStorage * Adobe Vault */ const BASE_PATH = '/dx_magento_qe'; - const KV_DATA = '/data'; - /** - * Local Vault - */ - //const BASE_PATH = '/secret'; - //const KV_DATA = '/data'; + const KV_DATA = 'data'; /** * Vault client * * @var Client */ - private static $client = null; + private $client = null; /** * Vault token @@ -51,13 +46,13 @@ class VaultStorage extends BaseStorage public function __construct($baseUrl, $token) { parent::__construct(); - if (null === self::$client) { + if (null === $this->client) { // Creating the client using Guzzle6 Transport and passing a custom url - self::$client = new Client(new Guzzle6Transport(['base_uri' => $baseUrl])); + $this->client = new Client(new Guzzle6Transport(['base_uri' => $baseUrl])); } $this->token = $token; if (!$this->authenticated()) { - throw new TestFrameworkException("Credential Vault: Cannot Authenticate"); + throw new TestFrameworkException("Credential vault is not used: cannot authenticate"); } } @@ -74,50 +69,48 @@ public function getEncryptedValue($key) return $value; } + if (MftfApplicationConfig::getConfig()->verboseEnabled()) { + LoggingUtil::getInstance()->getLogger(VaultStorage::class)->debug( + "Retrieving secret for key name {$key} from vault" + ); + } + + $reValue = null; try { - // Log here for verbose config + // Split vendor/key to construct secret path + list($vendor, $key) = explode('/', trim($key, '/'), 2); + $url = self::BASE_PATH + . (empty(self::KV_DATA) ? '' : '/' . self::KV_DATA) + . self::MFTF_PATH + . '/' + . $vendor + . '/' + . $key; + // Read value by key from vault + $value = $this->client->read($url)->getData()[self::KV_DATA][$key]; + // Encrypt value for return + $reValue = openssl_encrypt($value, parent::ENCRYPTION_ALGO, parent::$encodedKey, 0, parent::$iv); + parent::$cachedSecretData[$key] = $reValue; + } catch (\Exception $e) { if (MftfApplicationConfig::getConfig()->verboseEnabled()) { LoggingUtil::getInstance()->getLogger(VaultStorage::class)->debug( - "retrieving secret for key name {$key} from vault" + "Unable to read secret for key name {$key} from vault" ); } - } catch (\Exception $e) { - } - - // Retrieve from vault storage - if (!$this->authenticated()) { - return null; - } - - // Read value by key from vault - list($vendor, $key) = explode('/', trim($key, '/'), 2); - $url = self::BASE_PATH - . (empty(self::KV_DATA) ? '' : self::KV_DATA) - . self::MFTF_PATH - . '/' - . $vendor - . '/' - . $key; - $value = self::$client->read($url)->getData()['data'][$key]; - - if (empty($value)) { - return null; } - $eValue = openssl_encrypt($value, parent::ENCRYPTION_ALGO, parent::$encodedKey, 0, parent::$iv); - parent::$cachedSecretData[$key] = $eValue; - return $eValue; + return $reValue; } /** - * Check if vault token is still valid. + * Check if vault token is valid * * @return boolean */ private function authenticated() { try { - // Authenticating using token auth backend. - $authenticated = self::$client + // Authenticating using token auth backend + $authenticated = $this->client ->setAuthenticationStrategy(new TokenAuthenticationStrategy($this->token)) ->authenticate(); diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index 73a461148..cd81ade24 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -31,6 +31,7 @@ class ActionMergeUtil const DEFAULT_WAIT_ORDER = 'after'; const APPROVED_ACTIONS = ['fillField', 'magentoCLI', 'field']; const SECRET_MAPPING = ['fillField' => 'fillSecretField', 'magentoCLI' => 'magentoCLISecret']; + const CREDS_REGEX = "/{{_CREDS\.([\w|\/]+)}}/"; /** * Array holding final resulting steps @@ -149,7 +150,7 @@ private function actionAttributeContainsSecretRef($actionAttributes) return $this->actionAttributeContainsSecretRef($actionAttribute); } - preg_match_all("/{{_CREDS\.([\w|\/]+)}}/", $actionAttribute, $matches); + preg_match_all(self::CREDS_REGEX, $actionAttribute, $matches); if (!empty($matches[0])) { return true; diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index a43fedd5a..beff2bd9f 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -24,6 +24,7 @@ use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; +use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil; /** * Class TestGenerator @@ -1901,7 +1902,7 @@ private function resolveAllRuntimeReferences($args) { $runtimeReferenceRegex = [ "/{{_ENV\.([\w]+)}}/" => 'getenv', - "/{{_CREDS\.([\w|\/]+)}}/" => 'CredentialStore::getInstance()->getSecret' + ActionMergeUtil::CREDS_REGEX => 'CredentialStore::getInstance()->getSecret' ]; $argResult = $args; From 4c10d1c75cb662a38f60c1eeef40be95a47b5145 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Fri, 19 Jul 2019 14:19:40 -0500 Subject: [PATCH 4/7] MQE-1600: MFTF Vault integration - prefer credentials from local file over vault - address review comments --- .../DataGenerator/Handlers/CredentialStore.php | 6 +++--- .../DataGenerator/Handlers/SecretStorage/BaseStorage.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php index c122788cb..8385c2ec6 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -108,8 +108,8 @@ public function getSecret($key) } throw new TestFrameworkException( - "{$key} not defined in vault or .credentials file, " - . "please provide a value in order to use this secret in a test.\"." + "\"{$key}\" not defined in vault or .credentials file, " + . "please provide a value in order to use this secret in a test." ); } @@ -140,7 +140,7 @@ public function decryptAllSecretsInString($string) // Loop through storage to decrypt all occurrences from input string foreach (self::$credStoragePrecedence as $credType) { if (null !== $this->credStorage[$credType]) { - return $this->credStorage[$credType]->getAllDecryptedValues($string); + return $this->credStorage[$credType]->getAllDecryptedValuesInString($string); } } } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php index 22430e5b6..cb892a545 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/BaseStorage.php @@ -73,7 +73,7 @@ public function getDecryptedValue($value) * @param string $string * @return mixed */ - public function getAllDecryptedValues($string) + public function getAllDecryptedValuesInString($string) { $newString = $string; foreach (self::$cachedSecretData as $key => $secretValue) { From 36c5865f39690f53df5fd7b031bcdedca0a65c62 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Fri, 19 Jul 2019 14:29:43 -0500 Subject: [PATCH 5/7] MQE-1600: MFTF Vault integration - prefer credentials from local file over vault - address review comments --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f807d7415..913c74f48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: php php: + - 7.0 - 7.1 - 7.2 - 7.3 From 3f7fe8286054aa78bcfceb799e31c4fb08f504e3 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Fri, 19 Jul 2019 17:28:07 -0500 Subject: [PATCH 6/7] MQE-1600: MFTF Vault integration - Downgrade vault php version to be compatible to php 7.0 --- composer.json | 2 +- composer.lock | 34 +++++++------- .../Handlers/SecretStorage/VaultStorage.php | 3 +- .../SecretStorage/VaultTokenAuthStrategy.php | 47 +++++++++++++++++++ 4 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultTokenAuthStrategy.php diff --git a/composer.json b/composer.json index ddfafee8c..24fb91c92 100755 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "allure-framework/allure-codeception": "~1.3.0", "codeception/codeception": "~2.3.4 || ~2.4.0 ", "consolidation/robo": "^1.0.0", - "csharpru/vault-php": "^3.6", + "csharpru/vault-php": "~3.5.3", "csharpru/vault-php-guzzle6-transport": "^2.0", "flow/jsonpath": ">0.2", "fzaninotto/faker": "^1.6", diff --git a/composer.lock b/composer.lock index 767326d11..95f08abcd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c6cf409cdb5004e30eff742b235a857a", + "content-hash": "fb0f9a5c731e02404fb78c95aa647e46", "packages": [ { "name": "allure-framework/allure-codeception", @@ -763,21 +763,21 @@ }, { "name": "csharpru/vault-php", - "version": "3.6.0", + "version": "3.5.3", "source": { "type": "git", "url": "https://github.com/CSharpRU/vault-php.git", - "reference": "7a7376fe92ee33fe8ca15e728a6bbff1859bd789" + "reference": "04be9776310fe7d1afb97795645f95c21e6b4fcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CSharpRU/vault-php/zipball/7a7376fe92ee33fe8ca15e728a6bbff1859bd789", - "reference": "7a7376fe92ee33fe8ca15e728a6bbff1859bd789", + "url": "https://api.github.com/repos/CSharpRU/vault-php/zipball/04be9776310fe7d1afb97795645f95c21e6b4fcf", + "reference": "04be9776310fe7d1afb97795645f95c21e6b4fcf", "shasum": "" }, "require": { "cache/cache": "^0.4.0", - "doctrine/inflector": "~1.1", + "doctrine/inflector": "~1.1.0", "guzzlehttp/promises": "^1.3", "guzzlehttp/psr7": "^1.4", "psr/cache": "^1.0", @@ -807,7 +807,7 @@ } ], "description": "Best Vault client for PHP that you can find", - "time": "2018-05-21T07:05:32+00:00" + "time": "2018-04-28T04:52:17+00:00" }, { "name": "csharpru/vault-php-guzzle6-transport", @@ -1046,33 +1046,33 @@ }, { "name": "doctrine/inflector", - "version": "v1.3.0", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "4.*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { - "psr-4": { - "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1109,7 +1109,7 @@ "singularize", "string" ], - "time": "2018-01-09T20:05:19+00:00" + "time": "2015-11-06T14:35:42+00:00" }, { "name": "doctrine/instantiator", diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php index 316875b31..de80b83e3 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultStorage.php @@ -9,7 +9,6 @@ use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig; use Magento\FunctionalTestingFramework\Exceptions\TestFrameworkException; use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil; -use Vault\AuthenticationStrategies\TokenAuthenticationStrategy; use Vault\Client; use VaultTransports\Guzzle6Transport; @@ -111,7 +110,7 @@ private function authenticated() try { // Authenticating using token auth backend $authenticated = $this->client - ->setAuthenticationStrategy(new TokenAuthenticationStrategy($this->token)) + ->setAuthenticationStrategy(new VaultTokenAuthStrategy($this->token)) ->authenticate(); if ($authenticated) { diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultTokenAuthStrategy.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultTokenAuthStrategy.php new file mode 100644 index 000000000..716344ca1 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/SecretStorage/VaultTokenAuthStrategy.php @@ -0,0 +1,47 @@ +token = $token; + } + + /** + * Returns auth for further interactions with Vault + * + * @return Auth + * @throws TestFrameworkException + */ + public function authenticate() + { + try { + return new Auth(['clientToken' => $this->token]); + } catch (\Exception $e) { + throw new TestFrameworkException("Cannot authenticate Vault token."); + } + } +} From 6313da3a6a4ad92a805a4b985a72b519a72e5b6f Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Tue, 23 Jul 2019 09:52:38 -0500 Subject: [PATCH 7/7] MQE-1600: MFTF Vault integration - Simplify the logic to handle the storage array --- .../Handlers/CredentialStore.php | 54 +++++++------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php index 8385c2ec6..016360752 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php @@ -15,13 +15,6 @@ class CredentialStore const ARRAY_KEY_FOR_VAULT = 'vault'; const ARRAY_KEY_FOR_FILE = 'file'; - /** - * Numeric indexed array that defines the access precedence of credential storage - * - * @var array - */ - private static $credStoragePrecedence = [self::ARRAY_KEY_FOR_FILE, self::ARRAY_KEY_FOR_VAULT]; - /** * Credential storage array * @@ -58,6 +51,12 @@ public static function getInstance() */ private function __construct() { + // Initialize file storage + try { + $this->credStorage[self::ARRAY_KEY_FOR_FILE] = new FileStorage(); + } catch (TestFrameworkException $e) { + } + // Initialize vault storage $csBaseUrl = getenv('CREDENTIAL_VAULT_BASE_URL'); $csToken = getenv('CREDENTIAL_VAULT_TOKEN'); @@ -71,20 +70,11 @@ private function __construct() } } - // Initialize file storage - try { - $this->credStorage[self::ARRAY_KEY_FOR_FILE] = new FileStorage(); - } catch (TestFrameworkException $e) { + if (empty($this->credStorage)) { + throw new TestFrameworkException( + "No credential storage is properly configured. Please configure vault or .credentials file." + ); } - - foreach ($this->credStorage as $cred) { - if (null !== $cred) { - return; - } - } - throw new TestFrameworkException( - "No credential storage is properly configured. Please configure vault or .credentials file." - ); } /** @@ -96,14 +86,12 @@ private function __construct() */ public function getSecret($key) { - // Get secret data from storage according to defined precedence + // Get secret data from storage according to the order they are stored // File storage is preferred over vault storage to allow local secret value overriding remote secret value - foreach (self::$credStoragePrecedence as $credType) { - if (null !== $this->credStorage[$credType]) { - $value = $this->credStorage[$credType]->getEncryptedValue($key); - if (null !== $value) { - return $value; - } + foreach ($this->credStorage as $storage) { + $value = $storage->getEncryptedValue($key); + if (null !== $value) { + return $value; } } @@ -122,10 +110,8 @@ public function getSecret($key) public function decryptSecretValue($value) { // Loop through storage to decrypt value - foreach (self::$credStoragePrecedence as $credType) { - if (null !== $this->credStorage[$credType]) { - return $this->credStorage[$credType]->getDecryptedValue($value); - } + foreach ($this->credStorage as $storage) { + return $storage->getDecryptedValue($value); } } @@ -138,10 +124,8 @@ public function decryptSecretValue($value) public function decryptAllSecretsInString($string) { // Loop through storage to decrypt all occurrences from input string - foreach (self::$credStoragePrecedence as $credType) { - if (null !== $this->credStorage[$credType]) { - return $this->credStorage[$credType]->getAllDecryptedValuesInString($string); - } + foreach ($this->credStorage as $storage) { + return $storage->getAllDecryptedValuesInString($string); } } }