diff --git a/.github/workflows/end-to-end-auto-prepend-test-suite.yml b/.github/workflows/end-to-end-auto-prepend-test-suite.yml index 4915569..0b1e5d1 100644 --- a/.github/workflows/end-to-end-auto-prepend-test-suite.yml +++ b/.github/workflows/end-to-end-auto-prepend-test-suite.yml @@ -39,7 +39,7 @@ jobs: steps: - name: Install Magento 2 with DDEV - uses: julienloizelet/magento2-ddev-installation@v2.1.0 + uses: julienloizelet/magento2-ddev-installation@v2.1.1 with: php_version: ${{ matrix.php-version }} magento_version: ${{ matrix.m2-version }} diff --git a/.github/workflows/end-to-end-test-suite.yml b/.github/workflows/end-to-end-test-suite.yml index 0c86271..cacc7a9 100644 --- a/.github/workflows/end-to-end-test-suite.yml +++ b/.github/workflows/end-to-end-test-suite.yml @@ -44,7 +44,7 @@ jobs: steps: - name: Install Magento 2 with DDEV - uses: julienloizelet/magento2-ddev-installation@v2.1.0 + uses: julienloizelet/magento2-ddev-installation@v2.1.1 with: php_version: ${{ matrix.php-version }} magento_version: ${{ matrix.m2-version }} diff --git a/.github/workflows/installation-and-varnish-test-suite.yml b/.github/workflows/installation-and-varnish-test-suite.yml index 751e78d..d357218 100644 --- a/.github/workflows/installation-and-varnish-test-suite.yml +++ b/.github/workflows/installation-and-varnish-test-suite.yml @@ -42,7 +42,7 @@ jobs: steps: - name: Install Magento 2 with DDEV - uses: julienloizelet/magento2-ddev-installation@v2.1.0 + uses: julienloizelet/magento2-ddev-installation@v2.1.1 id: magento2-install with: php_version: ${{ matrix.php-version }} diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml new file mode 100644 index 0000000..c98254c --- /dev/null +++ b/.github/workflows/keepalive.yml @@ -0,0 +1,26 @@ +name: Keep Alive +on: + + schedule: + - cron: '0 3 * * 4' + +permissions: + contents: write + +jobs: + keep-alive: + + name: Keep Alive + runs-on: ubuntu-latest + + steps: + + - name: Clone project files + uses: actions/checkout@v3 + + # keepalive-workflow adds a dummy commit if there's no other action here, keeps + # GitHub from turning off tests after 60 days + - uses: gautamkrishnar/keepalive-workflow@v1 + with: + commit_message: "chore(*): Automated commit to keep the repository active" + time_elapsed: 50 diff --git a/.github/workflows/static-and-unit-test-suite.yml b/.github/workflows/static-and-unit-test-suite.yml index 0bd27dd..2d98cc7 100644 --- a/.github/workflows/static-and-unit-test-suite.yml +++ b/.github/workflows/static-and-unit-test-suite.yml @@ -5,6 +5,8 @@ on: - main paths-ignore: - '**.md' + schedule: + - cron: '25 02 * * THU' workflow_dispatch: permissions: @@ -39,7 +41,7 @@ jobs: steps: - name: Install Magento 2 with DDEV - uses: julienloizelet/magento2-ddev-installation@v2.1.0 + uses: julienloizelet/magento2-ddev-installation@v2.1.1 with: php_version: ${{ matrix.php-version }} magento_version: ${{ matrix.m2-version }} diff --git a/Block/Adminhtml/System/Config/Connection/Ping.php b/Block/Adminhtml/System/Config/Connection/Ping.php index 057afa4..f7acba4 100644 --- a/Block/Adminhtml/System/Config/Connection/Ping.php +++ b/Block/Adminhtml/System/Config/Connection/Ping.php @@ -89,6 +89,20 @@ class Ping extends Button */ protected $_useCurlField = 'crowdsec_bouncer_general_connection_use_curl'; + /** + * Api timeout field name + * + * @var string + */ + protected $_apiTimeoutField = 'crowdsec_bouncer_general_connection_api_timeout'; + + /** + * Api connect timeout field name + * + * @var string + */ + protected $_apiConnectTimeoutField = 'crowdsec_bouncer_general_connection_api_connect_timeout'; + /** @var string */ protected $template = 'CrowdSec_Bouncer::system/config/connection/ping.phtml'; /** @var string */ @@ -174,11 +188,32 @@ public function getUseCurlField(): string return $this->_useCurlField; } + /** + * Get api timeout field Name + * + * @return string + */ + public function getApiTimeoutField(): string + { + return $this->_apiTimeoutField; + } + + /** + * Get api connect timeout field Name + * + * @return string + */ + public function getApiConnectTimeoutField(): string + { + return $this->_apiConnectTimeoutField; + } + /** * Get the button and scripts contents * * @param AbstractElement $element * @return string + * @throws \InvalidArgumentException */ protected function _getElementHtml(AbstractElement $element): string { diff --git a/CHANGELOG.md b/CHANGELOG.md index d5a2684..5b8ab24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,34 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The [public API](https://semver.org/spec/v2.0.0.html#spec-item-1) for this project is defined by the set of +functions provided by the module. + + +## [2.1.0](https://github.com/crowdsecurity/cs-magento-bouncer/releases/tag/v2.1.0) - 2024-01-?? +[_Compare with previous release_](https://github.com/crowdsecurity/cs-magento-bouncer/compare/v2.0.0...v2.1.0) + + +### Changed + +- Encrypt bouncer key in database + +### Removed + +- Removed Events log feature + +### Added + +- Add `api_connect_timeout` configuration for `Curl` request handler +- Add `api_timeout` configuration + +### Fixed + +- Allow `crowdsec/symfony-cache:3.0.0` dependency to avoid composer conflict with some Magento 2.4.6 patch versions + +--- + + ## [2.0.0](https://github.com/crowdsecurity/cs-magento-bouncer/releases/tag/v2.0.0) - 2023-03-23 [_Compare with previous release_](https://github.com/crowdsecurity/cs-magento-bouncer/compare/v1.5.0...v2.0.0) diff --git a/Constants.php b/Constants.php index 306ac6d..28bee29 100644 --- a/Constants.php +++ b/Constants.php @@ -33,7 +33,7 @@ class Constants extends LibConstants { /** @var string The last version of this library */ - public const VERSION = 'v2.0.0'; + public const VERSION = 'v2.1.0'; /** @var string The user agent used to send request to Local API */ public const BASE_USER_AGENT = 'Magento 2 CrowdSec Bouncer/'.self::VERSION; diff --git a/Controller/Adminhtml/System/Config/Connection/Ping.php b/Controller/Adminhtml/System/Config/Connection/Ping.php index f08dcf2..81e6cd6 100644 --- a/Controller/Adminhtml/System/Config/Connection/Ping.php +++ b/Controller/Adminhtml/System/Config/Connection/Ping.php @@ -56,16 +56,18 @@ class Ping extends Action implements HttpPostActionInterface protected $helper; /** + * Constructor method. + * * @param Context $context * @param JsonFactory $resultJsonFactory * @param RegistryBouncer $registryBouncer * @param Helper $helper */ public function __construct( - Context $context, - JsonFactory $resultJsonFactory, + Context $context, + JsonFactory $resultJsonFactory, RegistryBouncer $registryBouncer, - Helper $helper + Helper $helper ) { parent::__construct($context); $this->resultJsonFactory = $resultJsonFactory; @@ -73,6 +75,28 @@ public function __construct( $this->helper = $helper; } + /** + * Create main suffixe message + * + * @param string $authType + * @param bool $useCurl + * @return string + */ + private function getMainSuffixeMessage(string $authType, bool $useCurl): string + { + $suffixMessageMain = ($authType === Constants::AUTH_TLS) ? + 'Auth type: TLS
Url: %1
Cert: %2
Key: %3
Verify peer: %4
+CA cert: %5
Use cURL: %6
Api timeout: %7' : + 'Auth type: Api key
Url: %1
Api key: %2
Use cURL: %3
Api timeout: %4'; + + if ($useCurl) { + $suffixMessageMain .= ($authType === Constants::AUTH_TLS) ? '
+ Api connection timeout: %8' : '
Api connection timeout: %5'; + } + + return $suffixMessageMain; + } + /** * Test connection * @@ -83,7 +107,9 @@ public function execute(): Json { $useCurl = ""; $tlsVerifyPeer = ""; - $authType =""; + $authType = ""; + $apiTimeout = ""; + $apiConnectTimeout = ""; try { $baseUri = $this->getRequest()->getParam('api_url'); $authType = $this->getRequest()->getParam('auth_type'); @@ -93,8 +119,13 @@ public function execute(): Json $tlsCaCert = ($authType === Constants::AUTH_TLS) ? $this->getRequest()->getParam('tls_ca_cert_path', "") : ""; $userAgent = Constants::BASE_USER_AGENT; - $apiKey = ($authType === Constants::AUTH_KEY) ? $this->getRequest()->getParam('bouncer_key') : ""; + $apiKey = ($authType === Constants::AUTH_KEY) ? + $this->getRequest()->getParam('bouncer_key') + : ""; $useCurl = (bool)$this->getRequest()->getParam('use_curl', false); + $apiTimeout = (int)$this->getRequest()->getParam('api_timeout', Constants::API_TIMEOUT); + $apiConnectTimeout = + (int)$this->getRequest()->getParam('api_connect_timeout', Constants::API_CONNECT_TIMEOUT); $configs = $this->helper->getBouncerConfigs(); $currentConfigs = [ 'api_url' => $baseUri, @@ -105,10 +136,12 @@ public function execute(): Json 'tls_ca_cert_path' => $this->helper->getVarFullPath($tlsCaCert), 'api_user_agent' => $userAgent, 'api_key' => $apiKey, - 'use_curl' => $useCurl + 'use_curl' => $useCurl, + 'api_timeout' => $apiTimeout, + 'api_connect_timeout' => $apiConnectTimeout, ]; - $useCurl = $useCurl ? __('true') : __('false'); + $useCurlMessage = $useCurl ? __('true') : __('false'); $tlsVerifyPeer = $tlsVerifyPeer ? __('true') : __('false'); $finalConfigs = array_merge($configs, $currentConfigs); $bouncer = $this->registryBouncer->create([ @@ -133,19 +166,25 @@ public function execute(): Json $resultJson = $this->resultJsonFactory->create(); + $suffixMessageMain = $this->getMainSuffixeMessage($authType, $useCurl); + $suffixMessage = ($authType === Constants::AUTH_TLS) ? '

' . __( - 'Auth type: TLS
Url: %1
Cert: %2
Key: %3
Verify peer: %4
CA cert: %5
Use cURL: %6', + $suffixMessageMain, $baseUri ?? "", $tlsCert ?? "", $tlsKey ?? "", $tlsVerifyPeer, $tlsCaCert ?? "", - $useCurl + $useCurlMessage ?? "", + $apiTimeout, + $apiConnectTimeout ) : '

' . __( - 'Auth type: Api key
Url: %1
Api key: %2
Use cURL: %3', + $suffixMessageMain, $baseUri ?? "", $apiKey ?? "", - $useCurl + $useCurlMessage ?? "", + $apiTimeout, + $apiConnectTimeout ); return $resultJson->setData([ diff --git a/Event/Event.php b/Event/Event.php deleted file mode 100644 index 8a38989..0000000 --- a/Event/Event.php +++ /dev/null @@ -1,130 +0,0 @@ -helper = $helper; - } - - /** - * Common data for all events - * - * @return array - */ - public function getBaseData(): array - { - return [ - 'type' => $this->type, - 'ip' => $this->helper->getRemoteIp(), - 'x-forwarded-for-ip' => $this->helper->getForwarderForIp(), - 'bouncer_agent' => Constants::BASE_USER_AGENT, - ]; - } - - /** - * Retrieve json schema from some file - * - * @param string $path - * @return SchemaContract - * @throws InvalidValue - * @throws \Swaggest\JsonSchema\Exception - */ - public function getJsonSchema(string $path = __DIR__ . DIRECTORY_SEPARATOR . 'event.json'): SchemaContract - { - if (!isset($this->jsonSchema[$path])) { - $schemaData = json_decode("{\"\$ref\": \"file://$path\"}"); - $this->jsonSchema[$path] = Schema::import($schemaData); - } - - return $this->jsonSchema[$path]; - } - - /** - * Check if data event is compliant with json schema - * - * @param array $eventData - * @return bool - * @throws LogicException - */ - public function validateEvent(array $eventData): bool - { - $result = false; - try { - $schema = $this->getJsonSchema(); - $schema->in((object)$eventData); - $result = true; - } catch (Exception $e) { - $this->helper->debug('Error while validating event', [ - 'type' => 'M2_EXCEPTION_WHILE_VALIDATING_EVENT', - 'message' => $e->getMessage(), - 'code' => $e->getCode(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - ]); - } - - return $result; - } -} diff --git a/Event/EventInterface.php b/Event/EventInterface.php deleted file mode 100644 index be10fb2..0000000 --- a/Event/EventInterface.php +++ /dev/null @@ -1,47 +0,0 @@ - null, 'api_key' => null, 'use_curl' => null, + 'api_timeout' => null, + 'api_connect_timeout' => null, 'is_admin_enabled' => null, 'is_api_enabled' => null, 'is_debug_log' => null, @@ -152,7 +132,6 @@ class Config extends AbstractHelper 'forced_test_forwarded_ip' => null, 'can_display_errors' => null, 'is_prod_log_disabled' => null, - 'is_events_log_enabled' => [], 'is_stream_mode' => null, 'refresh_cron_expr' => null, 'prune_cron_expr' => null, @@ -175,6 +154,14 @@ class Config extends AbstractHelper 'bouncing_level' => null, 'remediation_fallback' => null ]; + /** + * @var DirectoryList + */ + private $directoryList; + /** + * @var Json + */ + private $serializer; /** * Data constructor. @@ -184,8 +171,8 @@ class Config extends AbstractHelper * @param DirectoryList $directoryList */ public function __construct( - Context $context, - Json $serializer, + Context $context, + Json $serializer, DirectoryList $directoryList ) { parent::__construct($context); @@ -194,17 +181,18 @@ public function __construct( } /** - * Get api url config + * Get display errors config * - * @return string + * @return bool */ - public function getApiUrl(): string + public function canDisplayErrors(): bool { - if (!isset($this->_globals['api_url'])) { - $this->_globals['api_url'] = trim((string)$this->scopeConfig->getValue(self::XML_PATH_API_URL)); + if (!isset($this->_globals['can_display_errors'])) { + $this->_globals['can_display_errors'] = + (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_DISPLAY_ERRORS); } - return (string)$this->_globals['api_url']; + return (bool)$this->_globals['can_display_errors']; } /** @@ -222,170 +210,174 @@ public function getApiAuthType(): string } /** - * Get api key config + * Get api connection timeout config * - * @return string + * @return int */ - public function getApiKey(): string + public function getApiConnectTimeout(): int { - if (!isset($this->_globals['api_key'])) { - $this->_globals['api_key'] = trim((string)$this->scopeConfig->getValue(self::XML_PATH_API_KEY)); + if (!isset($this->_globals['api_connect_timeout'])) { + $this->_globals['api_connect_timeout'] = (int)$this->scopeConfig->getValue( + self::XML_PATH_API_CONNECT_TIMEOUT + ); } - return (string)$this->_globals['api_key']; + return (int)$this->_globals['api_connect_timeout']; } /** - * Get use curl config + * Get api key config * - * @return bool + * @return string */ - public function isUseCurl(): bool + public function getApiKey(): string { - if (!isset($this->_globals['use_curl'])) { - $this->_globals['use_curl'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_USE_CURL); + if (!isset($this->_globals['api_key'])) { + $this->_globals['api_key'] = (string)$this->scopeConfig->getValue(self::XML_PATH_API_KEY); } - return (bool)$this->_globals['use_curl']; + return (string)$this->_globals['api_key']; } /** - * Get enabled config for front + * Get api timeout config * - * @return bool + * @return int */ - public function isFrontEnabled(): bool + public function getApiTimeout(): int { - if (!isset($this->_storeviews['is_front_enabled'])) { - $this->_storeviews['is_front_enabled'] = (bool)$this->scopeConfig->getValue( - self::XML_PATH_FRONT_ENABLED, - ScopeInterface::SCOPE_STORE - ); + if (!isset($this->_globals['api_timeout'])) { + $this->_globals['api_timeout'] = (int)$this->scopeConfig->getValue(self::XML_PATH_API_TIMEOUT); } - return (bool)$this->_storeviews['is_front_enabled']; + return (int)$this->_globals['api_timeout']; } /** - * Get enabled config for admin + * Get api url config * - * @return bool + * @return string */ - public function isAdminEnabled(): bool + public function getApiUrl(): string { - if (!isset($this->_globals['is_admin_enabled'])) { - $this->_globals['is_admin_enabled'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_ADMIN_ENABLED); + if (!isset($this->_globals['api_url'])) { + $this->_globals['api_url'] = trim((string)$this->scopeConfig->getValue(self::XML_PATH_API_URL)); } - return (bool)$this->_globals['is_admin_enabled']; + return (string)$this->_globals['api_url']; } /** - * Get enabled config for api + * Get bad ip cache duration config * - * @return bool + * @return int */ - public function isApiEnabled(): bool + public function getBadIpCacheDuration(): int { - if (!isset($this->_globals['is_api_enabled'])) { - $this->_globals['is_api_enabled'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_API_ENABLED); + if (!isset($this->_globals['bad_ip_duration'])) { + $this->_globals['bad_ip_duration'] = (int)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_CACHE_BAD + ); } - return (bool)$this->_globals['is_api_enabled']; + return (int)$this->_globals['bad_ip_duration']; } /** - * Get debug log enabled config + * Get bouncing level config * - * @return bool + * @return string */ - public function isDebugLog(): bool + public function getBouncingLevel(): string { - if (!isset($this->_globals['is_debug_log'])) { - $this->_globals['is_debug_log'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_DEBUG_LOG); + if (!isset($this->_storeviews['bouncing_level'])) { + $this->_storeviews['bouncing_level'] = $this->scopeConfig->getValue( + self::XML_PATH_BOUNCING_LEVEL, + ScopeInterface::SCOPE_STORE + ); } - return (bool)$this->_globals['is_debug_log']; + return (string)$this->_storeviews['bouncing_level']; } /** - * Get prod log deactivation config + * Get cache technology config * - * @return bool + * @return string */ - public function isProdLogDisabled(): bool + public function getCacheTechnology(): string { - if (!isset($this->_globals['is_prod_log_disabled'])) { - $this->_globals['is_prod_log_disabled'] = - (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_DISABLE_PROD_LOG); + if (!isset($this->_globals['cache_technology'])) { + $this->_globals['cache_technology'] = (string)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_CACHE_TECHNOLOGY + ); } - return (bool)$this->_globals['is_prod_log_disabled']; + return (string)$this->_globals['cache_technology']; } /** - * Get events log enabled config + * Get captcha cache duration config * - * @param string $process - * @return bool + * @return int */ - public function isEventsLogEnabled(string $process): bool + public function getCaptchaCacheDuration(): int { - if (!isset($this->_globals['is_events_log_enabled'][$process])) { - $enabled = (bool)$this->scopeConfig->getValue(self::XML_PATH_EVENTS_LOG_ROOT . 'enabled'); - $this->_globals['is_events_log_enabled'][$process] = - $enabled && $this->scopeConfig->getValue(self::XML_PATH_EVENTS_LOG_ROOT . $process); + if (!isset($this->_globals['captcha_duration'])) { + $this->_globals['captcha_duration'] = (int)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_CACHE_CAPTCHA + ); } - return (bool)$this->_globals['is_events_log_enabled'][$process]; + return (int)$this->_globals['captcha_duration']; } /** - * Get display errors config + * Get clean ip cache duration config * - * @return bool + * @return int */ - public function canDisplayErrors(): bool + public function getCleanIpCacheDuration(): int { - if (!isset($this->_globals['can_display_errors'])) { - $this->_globals['can_display_errors'] = - (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_DISPLAY_ERRORS); + if (!isset($this->_globals['clean_ip_duration'])) { + $this->_globals['clean_ip_duration'] = (int)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_CACHE_CLEAN + ); } - return (bool)$this->_globals['can_display_errors']; + return (int)$this->_globals['clean_ip_duration']; } /** - * Get bouncing level config + * Get forced test forwarded ip config * * @return string */ - public function getBouncingLevel(): string + public function getForcedTestForwardedIp(): string { - if (!isset($this->_storeviews['bouncing_level'])) { - $this->_storeviews['bouncing_level'] = $this->scopeConfig->getValue( - self::XML_PATH_BOUNCING_LEVEL, - ScopeInterface::SCOPE_STORE + if (!isset($this->_globals['forced_test_forwarded_ip'])) { + $this->_globals['forced_test_forwarded_ip'] = (string)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_FORCED_TEST_FWD_IP ); } - return (string)$this->_storeviews['bouncing_level']; + return (string)$this->_globals['forced_test_forwarded_ip']; } /** - * Get the absolute path of a var file + * Get forced test ip config * - * @param string $relativePath * @return string - * @throws BouncerException */ - public function getVarFullPath(string $relativePath): string + public function getForcedTestIp(): string { - try { - return $this->directoryList->getPath(DirectoryList::VAR_DIR) . '/' . ltrim($relativePath, '/'); - } catch (Exception $e) { - throw new BouncerException($e->getMessage()); + if (!isset($this->_globals['forced_test_ip'])) { + $this->_globals['forced_test_ip'] = (string)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_FORCED_TEST_IP + ); } + + return (string)$this->_globals['forced_test_ip']; } /** @@ -421,78 +413,67 @@ public function getGeolocation(): array } /** - * Get TLS authentication config + * Get geolocation cache duration config * - * @return array - * @throws BouncerException + * @return int */ - public function getTLS(): array + public function getGeolocationCacheDuration(): int { - if (!isset($this->_globals['tls'])) { - - $result = [ - 'tls_cert_path' => $this->getVarFullPath( - (string)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_CERT) - ), - 'tls_key_path' => $this->getVarFullPath( - (string)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_KEY) - ), - 'tls_verify_peer' => (bool)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_VERIFY_PEER), - 'tls_ca_cert_path' => $this->getVarFullPath( - (string)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_CA_CERT) - ) - ]; - - $this->_globals['tls'] = $result; + if (!isset($this->_globals['geolocation_duration'])) { + $this->_globals['geolocation_duration'] = (int)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_CACHE_GEO + ); } - return (array)$this->_globals['tls']; + return (int)$this->_globals['geolocation_duration']; } /** - * Get forced test ip config + * Get Memcached DSN config * * @return string */ - public function getForcedTestIp(): string + public function getMemcachedDSN(): string { - if (!isset($this->_globals['forced_test_ip'])) { - $this->_globals['forced_test_ip'] = (string)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_FORCED_TEST_IP + if (!isset($this->_globals['memcached_dsn'])) { + $this->_globals['memcached_dsn'] = (string)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_CACHE_MEMCACHED_DSN ); } - return (string)$this->_globals['forced_test_ip']; + return (string)$this->_globals['memcached_dsn']; } /** - * Get forced test forwarded ip config + * Get pruning cron schedule expression config * * @return string */ - public function getForcedTestForwardedIp(): string + public function getPruneCronExpr(): string { - if (!isset($this->_globals['forced_test_forwarded_ip'])) { - $this->_globals['forced_test_forwarded_ip'] = (string)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_FORCED_TEST_FWD_IP + if (!isset($this->_globals['prune_cron_expr'])) { + $this->_globals['prune_cron_expr'] = (string)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_PRUNE_CRON_EXPR ); } - return (string)$this->_globals['forced_test_forwarded_ip']; + return (string)$this->_globals['prune_cron_expr']; } /** - * Get stream mode config + * Get Redis DSN config * - * @return bool + * @return string */ - public function isStreamModeEnabled(): bool + public function getRedisDSN(): string { - if (!isset($this->_globals['is_stream_mode'])) { - $this->_globals['is_stream_mode'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_MODE_STREAM); + if (!isset($this->_globals['redis_dsn'])) { + $this->_globals['redis_dsn'] = (string)$this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_CACHE_REDIS_DSN + ); } - return (bool)$this->_globals['is_stream_mode']; + return (string)$this->_globals['redis_dsn']; } /** @@ -512,165 +493,183 @@ public function getRefreshCronExpr(): string } /** - * Get pruning cron schedule expression config + * Get bouncing level config * * @return string */ - public function getPruneCronExpr(): string + public function getRemediationFallback(): string { - if (!isset($this->_globals['prune_cron_expr'])) { - $this->_globals['prune_cron_expr'] = (string)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_PRUNE_CRON_EXPR + if (!isset($this->_storeviews['remediation_fallback'])) { + $this->_storeviews['remediation_fallback'] = $this->scopeConfig->getValue( + self::XML_PATH_ADVANCED_REMEDIATION_FALLBACK, + ScopeInterface::SCOPE_STORE ); } - return (string)$this->_globals['prune_cron_expr']; + return (string)$this->_storeviews['remediation_fallback']; } /** - * Get cache technology config + * Get TLS authentication config * - * @return string + * @return array + * @throws BouncerException */ - public function getCacheTechnology(): string + public function getTLS(): array { - if (!isset($this->_globals['cache_technology'])) { - $this->_globals['cache_technology'] = (string)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_CACHE_TECHNOLOGY - ); + if (!isset($this->_globals['tls'])) { + $result = [ + 'tls_cert_path' => $this->getVarFullPath( + (string)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_CERT) + ), + 'tls_key_path' => $this->getVarFullPath( + (string)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_KEY) + ), + 'tls_verify_peer' => (bool)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_VERIFY_PEER), + 'tls_ca_cert_path' => $this->getVarFullPath( + (string)$this->scopeConfig->getValue(self::XML_PATH_API_TLS_CA_CERT) + ) + ]; + + $this->_globals['tls'] = $result; } - return (string)$this->_globals['cache_technology']; + return (array)$this->_globals['tls']; } /** - * Get Redis DSN config + * Get trusted forwarded ips config * - * @return string + * @return array + * @throws InvalidArgumentException */ - public function getRedisDSN(): string + public function getTrustedForwardedIps(): array { - if (!isset($this->_globals['redis_dsn'])) { - $this->_globals['redis_dsn'] = (string)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_CACHE_REDIS_DSN - ); + if (!isset($this->_globals['trusted_forwarded_ip'])) { + $trustedForwardedIps = $this->scopeConfig->getValue(self::TRUSTED_FORWARD_IPS_PATH); + + $this->_globals['trusted_forwarded_ip'] = + !empty($trustedForwardedIps) ? $this->serializer->unserialize($trustedForwardedIps) : []; } - return (string)$this->_globals['redis_dsn']; + return (array)$this->_globals['trusted_forwarded_ip']; } /** - * Get Memcached DSN config + * Get the absolute path of a var file * + * @param string $relativePath * @return string + * @throws BouncerException */ - public function getMemcachedDSN(): string + public function getVarFullPath(string $relativePath): string { - if (!isset($this->_globals['memcached_dsn'])) { - $this->_globals['memcached_dsn'] = (string)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_CACHE_MEMCACHED_DSN - ); + try { + return $this->directoryList->getPath(DirectoryList::VAR_DIR) . '/' . ltrim($relativePath, '/'); + } catch (Exception $e) { + throw new BouncerException($e->getMessage()); } - - return (string)$this->_globals['memcached_dsn']; } /** - * Get clean ip cache duration config + * Get enabled config for admin * - * @return int + * @return bool */ - public function getCleanIpCacheDuration(): int + public function isAdminEnabled(): bool { - if (!isset($this->_globals['clean_ip_duration'])) { - $this->_globals['clean_ip_duration'] = (int)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_CACHE_CLEAN - ); + if (!isset($this->_globals['is_admin_enabled'])) { + $this->_globals['is_admin_enabled'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_ADMIN_ENABLED); } - return (int)$this->_globals['clean_ip_duration']; + return (bool)$this->_globals['is_admin_enabled']; } /** - * Get bad ip cache duration config + * Get enabled config for api * - * @return int + * @return bool */ - public function getBadIpCacheDuration(): int + public function isApiEnabled(): bool { - if (!isset($this->_globals['bad_ip_duration'])) { - $this->_globals['bad_ip_duration'] = (int)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_CACHE_BAD - ); + if (!isset($this->_globals['is_api_enabled'])) { + $this->_globals['is_api_enabled'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_API_ENABLED); } - return (int)$this->_globals['bad_ip_duration']; + return (bool)$this->_globals['is_api_enabled']; } /** - * Get captcha cache duration config + * Get debug log enabled config * - * @return int + * @return bool */ - public function getCaptchaCacheDuration(): int + public function isDebugLog(): bool { - if (!isset($this->_globals['captcha_duration'])) { - $this->_globals['captcha_duration'] = (int)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_CACHE_CAPTCHA - ); + if (!isset($this->_globals['is_debug_log'])) { + $this->_globals['is_debug_log'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_DEBUG_LOG); } - return (int)$this->_globals['captcha_duration']; + return (bool)$this->_globals['is_debug_log']; } /** - * Get geolocation cache duration config + * Get enabled config for front * - * @return int + * @return bool */ - public function getGeolocationCacheDuration(): int + public function isFrontEnabled(): bool { - if (!isset($this->_globals['geolocation_duration'])) { - $this->_globals['geolocation_duration'] = (int)$this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_CACHE_GEO + if (!isset($this->_storeviews['is_front_enabled'])) { + $this->_storeviews['is_front_enabled'] = (bool)$this->scopeConfig->getValue( + self::XML_PATH_FRONT_ENABLED, + ScopeInterface::SCOPE_STORE ); } - return (int)$this->_globals['geolocation_duration']; + return (bool)$this->_storeviews['is_front_enabled']; } /** - * Get bouncing level config + * Get prod log deactivation config * - * @return string + * @return bool */ - public function getRemediationFallback(): string + public function isProdLogDisabled(): bool { - if (!isset($this->_storeviews['remediation_fallback'])) { - $this->_storeviews['remediation_fallback'] = $this->scopeConfig->getValue( - self::XML_PATH_ADVANCED_REMEDIATION_FALLBACK, - ScopeInterface::SCOPE_STORE - ); + if (!isset($this->_globals['is_prod_log_disabled'])) { + $this->_globals['is_prod_log_disabled'] = + (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_DISABLE_PROD_LOG); } - return (string)$this->_storeviews['remediation_fallback']; + return (bool)$this->_globals['is_prod_log_disabled']; } /** - * Get trusted forwarded ips config + * Get stream mode config * - * @return array - * @throws InvalidArgumentException + * @return bool */ - public function getTrustedForwardedIps(): array + public function isStreamModeEnabled(): bool { - if (!isset($this->_globals['trusted_forwarded_ip'])) { - $trustedForwardedIps = $this->scopeConfig->getValue(self::TRUSTED_FORWARD_IPS_PATH); + if (!isset($this->_globals['is_stream_mode'])) { + $this->_globals['is_stream_mode'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_ADVANCED_MODE_STREAM); + } - $this->_globals['trusted_forwarded_ip'] = - !empty($trustedForwardedIps) ? $this->serializer->unserialize($trustedForwardedIps) : []; + return (bool)$this->_globals['is_stream_mode']; + } + + /** + * Get use curl config + * + * @return bool + */ + public function isUseCurl(): bool + { + if (!isset($this->_globals['use_curl'])) { + $this->_globals['use_curl'] = (bool)$this->scopeConfig->getValue(self::XML_PATH_USE_CURL); } - return (array)$this->_globals['trusted_forwarded_ip']; + return (bool)$this->_globals['use_curl']; } } diff --git a/Helper/Data.php b/Helper/Data.php index 1faaee0..08bdee0 100644 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -33,7 +33,6 @@ use LogicException; use Magento\Framework\App\Helper\Context; use Magento\Framework\App\Area; -use Magento\Framework\Exception\FileSystemException; use Magento\Store\Model\ScopeInterface; use CrowdSec\Bouncer\Logger\Logger; use CrowdSec\Bouncer\Logger\Handlers\DebugFactory as DebugHandler; @@ -135,7 +134,7 @@ public function error($message, array $context = []): void * * @return array * @throws LogicException - * @throws FileSystemException|BouncerException + * @throws BouncerException */ public function getBouncerConfigs(): array { @@ -154,7 +153,8 @@ public function getBouncerConfigs(): array 'api_key' => $this->getApiKey(), 'user_agent_version' => Constants::VERSION, 'user_agent_suffix' => 'Magento2', - 'api_timeout' => Constants::API_TIMEOUT, + 'api_timeout' => $this->getApiTimeout(), + 'api_connect_timeout' => $this->getApiConnectTimeout(), 'use_curl' => $this->isUseCurl(), // Debug 'debug_mode' => $this->isDebugLog(), diff --git a/Helper/Event.php b/Helper/Event.php deleted file mode 100644 index 6404729..0000000 --- a/Helper/Event.php +++ /dev/null @@ -1,74 +0,0 @@ -_eventLogger = $eventLogger; - } - - /** - * Event Loger getter - * - * @return EventLogger - */ - public function getEventLogger(): EventLogger - { - return $this->_eventLogger; - } -} diff --git a/Logger/EventLogger.php b/Logger/EventLogger.php deleted file mode 100644 index 05768f8..0000000 --- a/Logger/EventLogger.php +++ /dev/null @@ -1,32 +0,0 @@ -response) { $noCacheControl = 'no-store, no-cache, must-revalidate, max-age=0,post-check=0, pre-check=0'; diff --git a/Observer/Customer.php b/Observer/Customer.php deleted file mode 100644 index a1cec22..0000000 --- a/Observer/Customer.php +++ /dev/null @@ -1,73 +0,0 @@ - (string)$customer->getId()] : []; - } - - /** - * Event observer execution - * - * @param Observer $observer - * @return $this - * @throws LogicException - */ - public function execute(Observer $observer): Customer - { - if ($this->helper->isEventsLogEnabled($this->process)) { - $customer = $observer->getCustomer(); - $baseData = $this->getBaseData(); - $dataObjects = ['customer' => $customer]; - $eventData = $this->getEventData($dataObjects); - $finalData = array_merge($baseData, $eventData); - if ($this->validateEvent($finalData)) { - $this->helper->getEventLogger()->info('', $finalData); - } - - } - - return $this; - } -} diff --git a/Observer/Customer/Login.php b/Observer/Customer/Login.php deleted file mode 100644 index c44d676..0000000 --- a/Observer/Customer/Login.php +++ /dev/null @@ -1,43 +0,0 @@ - (string) $order->getIncrementId(), - 'customer_id' => (string) $order->getCustomerId(), - 'quote_id' => (string) $order->getQuoteId(), - ] : []; - } - - /** - * Event observer execution - * - * @param Observer $observer - * @return $this - * @throws LogicException - */ - public function execute(Observer $observer): Order - { - if ($this->helper->isEventsLogEnabled($this->process)) { - $order = $observer->getOrder(); - $baseData = $this->getBaseData(); - $dataObjects = ['order' => $order]; - $eventData = $this->getEventData($dataObjects); - $finalData = array_merge($baseData, $eventData); - if ($this->validateEvent($finalData)) { - $this->helper->getEventLogger()->info('', $finalData); - } - } - - return $this; - } -} diff --git a/Observer/Order/PlaceAfter.php b/Observer/Order/PlaceAfter.php deleted file mode 100644 index c859d73..0000000 --- a/Observer/Order/PlaceAfter.php +++ /dev/null @@ -1,43 +0,0 @@ - $payment->getMethod()] : []; - } - - /** - * Event observer execution - * - * @param Observer $observer - * @return $this - * @throws LogicException - */ - public function execute(Observer $observer): Payment - { - if ($this->helper->isEventsLogEnabled($this->process)) { - $payment = $observer->getPayment(); - $baseData = $this->getBaseData(); - $dataObjects = ['payment' => $payment]; - $eventData = $this->getEventData($dataObjects); - $finalData = array_merge($baseData, $eventData); - if ($this->validateEvent($finalData)) { - $this->helper->getEventLogger()->info('', $finalData); - } - } - - return $this; - } -} diff --git a/Observer/Payment/PlaceEnd.php b/Observer/Payment/PlaceEnd.php deleted file mode 100644 index d094447..0000000 --- a/Observer/Payment/PlaceEnd.php +++ /dev/null @@ -1,43 +0,0 @@ - (string) $product->getId()] : []; - $quoteItemData = $quoteItem ? ['quote_id' => (string) $quoteItem->getQuoteId()] : []; - - return array_merge($productData, $quoteItemData); - } - - /** - * Event observer execution - * - * @param Observer $observer - * @return $this - * @throws LogicException - */ - public function execute(Observer $observer): Quote - { - if ($this->helper->isEventsLogEnabled($this->process)) { - $product = $observer->getProduct(); - $quoteItem = $observer->getQuoteItem(); - $baseData = $this->getBaseData(); - $dataObjects = ['product' => $product, 'quote_item' => $quoteItem]; - $eventData = $this->getEventData($dataObjects); - $finalData = array_merge($baseData, $eventData); - if ($this->validateEvent($finalData)) { - $this->helper->getEventLogger()->info('', $finalData); - } - } - - return $this; - } -} diff --git a/Observer/Quote/AddProductAfter.php b/Observer/Quote/AddProductAfter.php deleted file mode 100644 index c52b77e..0000000 --- a/Observer/Quote/AddProductAfter.php +++ /dev/null @@ -1,42 +0,0 @@ -helper->isEventsLogEnabled($this->process)) { - $baseData = $this->getBaseData(); - $eventData = $this->getEventData(); - $finalData = array_merge($baseData, $eventData); - if ($this->validateEvent($finalData)) { - $this->helper->getEventLogger()->info('', $finalData); - } - } - - return $this; - } -} diff --git a/Observer/User/LoginFailed.php b/Observer/User/LoginFailed.php deleted file mode 100644 index 8c6a1b8..0000000 --- a/Observer/User/LoginFailed.php +++ /dev/null @@ -1,42 +0,0 @@ -messageManager = $messageManager; $this->helper = $helper; @@ -98,9 +98,9 @@ public function __construct( * @return array[] * @throws BouncerException */ - protected function _getTLS($subject, $isTLS) + protected function _getTLS($subject, $isTLS): array { - $result = ['old'=>[], 'new' => []]; + $result = ['old' => [], 'new' => []]; if ($isTLS) { $oldTls = $this->helper->getTLS(); $oldTlsCert = $oldTls['tls_cert_path']; @@ -128,7 +128,6 @@ protected function _getTLS($subject, $isTLS) ]; $result['old'] = $oldTls; - } return $result; @@ -282,10 +281,10 @@ private function getCurrentValue($subject, $saved) * @throws BouncerException|CacheException */ public function _handleStreamMode( - bool $oldStreamMode, - bool $newStreamMode, - bool $refreshCronExprChanged, - bool $cacheChanged, + bool $oldStreamMode, + bool $newStreamMode, + bool $refreshCronExprChanged, + bool $cacheChanged, string $newCacheSystem, string $newRedisDsn, string $newMemcachedDsn, @@ -329,10 +328,10 @@ public function _handleStreamMode( * @throws CacheException */ protected function _handleRefresh( - bool $oldStreamMode, - bool $newStreamMode, - bool $refreshCronExprChanged, - bool $cacheChanged, + bool $oldStreamMode, + bool $newStreamMode, + bool $refreshCronExprChanged, + bool $cacheChanged, string $newCacheSystem, string $newRedisDsn, string $newMemcachedDsn, @@ -367,9 +366,9 @@ protected function _handleRefresh( * @throws BouncerException */ protected function _handleRefreshCronExpr( - bool $oldStreamMode, - bool $newStreamMode, - bool $cronExprChanged, + bool $oldStreamMode, + bool $newStreamMode, + bool $cronExprChanged, string $newCronExpr ) { if ($oldStreamMode !== $newStreamMode && $newStreamMode === false && $newCronExpr !== self::CRON_DISABLE) { @@ -390,7 +389,7 @@ protected function _handleRefreshCronExpr( $this->helper->validateCronExpr($newCronExpr); } catch (Exception $e) { $this->messageManager->getMessages(true); - throw new BouncerException("Refresh cron expression ($newCronExpr) is not valid: ". $e->getMessage()); + throw new BouncerException("Refresh cron expression ($newCronExpr) is not valid: " . $e->getMessage()); } } } @@ -408,7 +407,7 @@ protected function _handleRefreshCronExpr( protected function _handlePruneCronExpr( string $oldCacheSystem, string $newCacheSystem, - bool $cronExprChanged, + bool $cronExprChanged, string $newCronExpr ) { if ($oldCacheSystem !== $newCacheSystem && @@ -431,7 +430,7 @@ protected function _handlePruneCronExpr( $this->helper->validateCronExpr($newCronExpr); } catch (Exception $e) { $this->messageManager->getMessages(true); - throw new BouncerException("Pruning cron expression ($newCronExpr) is not valid: ". $e->getMessage()); + throw new BouncerException("Pruning cron expression ($newCronExpr) is not valid: " . $e->getMessage()); } } } @@ -450,12 +449,12 @@ protected function _handleConnectionChanges( ) { // Test connection if params changed if ($oldConnection != $newConnection && !empty($newConnection['api_url'])) { - $finalApiKey = $newConnection['api_key']??""; - $finalCert = $newConnection['tls']['tls_cert_path']??""; - $finalKey = $newConnection['tls']['tls_key_path']??"" ; - $finalVerify = $newConnection['tls']['tls_verify_peer']??false; - $finalCaCert = $newConnection['tls']['tls_ca_cert_path']??""; - $finalUseCurl = $newConnection['use_curl']??false; + $finalApiKey = $newConnection['api_key'] ?? ""; + $finalCert = $newConnection['tls']['tls_cert_path'] ?? ""; + $finalKey = $newConnection['tls']['tls_key_path'] ?? ""; + $finalVerify = $newConnection['tls']['tls_verify_peer'] ?? false; + $finalCaCert = $newConnection['tls']['tls_ca_cert_path'] ?? ""; + $finalUseCurl = $newConnection['use_curl'] ?? false; try { $configs = $this->helper->getBouncerConfigs(); $currentConfigs = [ @@ -480,10 +479,10 @@ protected function _handleConnectionChanges( $message = 'Connection test failed with
auth_type=' . $newConnection['auth_type'] . '
url=' . $newConnection['api_url'] . '
use curl=' . (!empty($finalUseCurl) ? 'true' : 'false') - . '
api key=' . ($finalApiKey ?? "") + . '
api key=******' . '
tls cert path=' . ($finalCert ?? "") . '
tls key path=' . ($finalKey ?? "") - . '
tls ca cert path=' . ($finalCaCert?? "") + . '
tls ca cert path=' . ($finalCaCert ?? "") . '
tls verify peer=' . (!empty($finalVerify) ? 'true' : 'false') . '
: '; throw new BouncerException($message . $e->getMessage()); @@ -504,8 +503,8 @@ protected function _handleConnectionChanges( * @throws LogicException */ protected function _handleNewClearCache( - bool $oldStreamMode, - bool $newStreamMode, + bool $oldStreamMode, + bool $newStreamMode, string $newCacheSystem, string $newRedisDsn, string $newMemcachedDsn, @@ -536,7 +535,7 @@ protected function _handleNewClearCache( * @throws LogicException */ protected function _handleOldClearCache( - bool $cacheChanged, + bool $cacheChanged, string $oldCacheSystem, string $oldMemcachedDsn, string $oldRedisDsn, @@ -558,7 +557,7 @@ protected function _handleOldClearCache( * @throws InvalidArgumentException|LogicException|BouncerException */ protected function _handleTestCache( - bool $cacheChanged, + bool $cacheChanged, string $cacheSystem, string $memcachedDsn, string $redisDsn, @@ -726,7 +725,7 @@ private function hasDsnChanged( * @return void */ private function displayCacheClearMessage( - bool $clearCacheResult, + bool $clearCacheResult, Phrase $cacheLabel, Phrase $preMessage = null ): void { diff --git a/Plugin/Customer/AccountCreatePostController.php b/Plugin/Customer/AccountCreatePostController.php deleted file mode 100644 index 3ed5ee6..0000000 --- a/Plugin/Customer/AccountCreatePostController.php +++ /dev/null @@ -1,81 +0,0 @@ -helper->isEventsLogEnabled($this->process)) { - $baseData = $this->getBaseData(); - $eventData = $this->getEventData(); - $finalData = array_merge($baseData, $eventData); - if ($this->validateEvent($finalData)) { - $this->helper->getEventLogger()->info('', $finalData); - } - } - } -} diff --git a/Plugin/Customer/AccountManagement.php b/Plugin/Customer/AccountManagement.php deleted file mode 100644 index c7651ec..0000000 --- a/Plugin/Customer/AccountManagement.php +++ /dev/null @@ -1,80 +0,0 @@ -helper->isEventsLogEnabled($this->process)) { - $baseData = $this->getBaseData(); - $eventData = $this->getEventData(); - $finalData = array_merge($baseData, $eventData); - if ($this->validateEvent($finalData)) { - $this->helper->getEventLogger()->info('', $finalData); - } - } - } -} diff --git a/Setup/Patch/Data/EncryptBouncerKey.php b/Setup/Patch/Data/EncryptBouncerKey.php new file mode 100644 index 0000000..a78c427 --- /dev/null +++ b/Setup/Patch/Data/EncryptBouncerKey.php @@ -0,0 +1,82 @@ +encryptor = $encryptor; + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * Apply patch. + * + * @return void + */ + public function apply() + { + $bouncerKeyPath = Config::XML_PATH_API_KEY; + $configTable = $this->moduleDataSetup->getTable('core_config_data'); + $select = $this->moduleDataSetup->getConnection()->select() + ->from($configTable) + ->where('path = ?', $bouncerKeyPath); + $config = $this->moduleDataSetup->getConnection()->fetchAll($select); + if (!empty($config)) { + $value = $config[0]['value'] ?? ''; + if ($value) { + $this->moduleDataSetup->getConnection()->update( + $configTable, + ['value' => $this->encryptor->encrypt($value)], + ['path = ?' => $bouncerKeyPath] + ); + } + } + } + + /** + * Retrieve dependencies. + * + * @return array|string[] + */ + public static function getDependencies() + { + return []; + } + + /** + * Retrieve aliases + * + * @return array|string[] + */ + public function getAliases() + { + return []; + } +} diff --git a/Test/EndToEnd/__scripts__/run-tests.sh b/Test/EndToEnd/__scripts__/run-tests.sh index 3100a27..9366bee 100755 --- a/Test/EndToEnd/__scripts__/run-tests.sh +++ b/Test/EndToEnd/__scripts__/run-tests.sh @@ -43,7 +43,7 @@ HOSTNAME=$(ddev exec printenv DDEV_HOSTNAME | sed 's/\r//g') M2VERSION=$(ddev exec printenv DDEV_PROJECT | sed 's/\r//g') M2_URL=https://$HOSTNAME PROXY_IP=$(ddev find-ip ddev-router) -BOUNCER_KEY=$(ddev exec bin/magento config:show crowdsec_bouncer/general/connection/api_key | sed 's/\r//g') +BOUNCER_KEY=$(ddev exec cat .ddev/commands/host/bouncer_key_plain.txt | sed 's/\r//g') JEST_PARAMS="--bail=true --runInBand --verbose" TLS_PATH="crowdsec/tls" # Relative to var path # If FAIL_FAST, will exit on first individual test fail @@ -68,7 +68,7 @@ case $TYPE in "docker") DEBUG_STRING="" - YARN_PATH="./var/www/html/my-own-modules/crowdsec-bouncer/Test/EndToEnd" + YARN_PATH="./my-own-modules/crowdsec-bouncer/Test/EndToEnd" COMMAND="ddev exec -s playwright yarn --cwd ${YARN_PATH} cross-env" LAPI_URL_FROM_PLAYWRIGHT=https://crowdsec:8080 CURRENT_IP=$(ddev find-ip playwright) @@ -80,7 +80,7 @@ case $TYPE in "ci") DEBUG_STRING="DEBUG=pw:api" - YARN_PATH="./var/www/html/my-own-modules/crowdsec-bouncer/Test/EndToEnd" + YARN_PATH="./my-own-modules/crowdsec-bouncer/Test/EndToEnd" COMMAND="ddev exec -s playwright xvfb-run --auto-servernum -- yarn --cwd ${YARN_PATH} cross-env" LAPI_URL_FROM_PLAYWRIGHT=https://crowdsec:8080 CURRENT_IP=$(ddev find-ip playwright) diff --git a/Test/EndToEnd/__scripts__/test-init.sh b/Test/EndToEnd/__scripts__/test-init.sh index ec1ec06..2cf030e 100755 --- a/Test/EndToEnd/__scripts__/test-init.sh +++ b/Test/EndToEnd/__scripts__/test-init.sh @@ -9,5 +9,5 @@ if ! ddev --version >/dev/null 2>&1; then exit 1 fi -ddev exec -s playwright yarn --cwd ./var/www/html/my-own-modules/crowdsec-bouncer/Test/EndToEnd --force && \ +ddev exec -s playwright yarn --cwd ./my-own-modules/crowdsec-bouncer/Test/EndToEnd --force && \ ddev exec -s playwright yarn global add cross-env diff --git a/Test/EndToEnd/__tests__/2-live-mode.js b/Test/EndToEnd/__tests__/2-live-mode.js index 1f2eadd..b8a1cd9 100644 --- a/Test/EndToEnd/__tests__/2-live-mode.js +++ b/Test/EndToEnd/__tests__/2-live-mode.js @@ -200,7 +200,11 @@ describe(`Test cURL in Live mode`, () => { await page.click("#crowdsec_bouncer_general_connection_test"); await expect(page).toMatchText( "#lapi_ping_result", - /Connection test result: success.*Use cURL: true/, + /Use cURL: true/, + ); + await expect(page).toMatchText( + "#lapi_ping_result", + /Connection test result: success/, ); await onAdminSaveSettings(); }); @@ -291,7 +295,11 @@ describe(`Test TLS auth in Live mode`, () => { await wait(2000); await expect(page).toMatchText( "#lapi_ping_result", - /Connection test result: success.*Auth type: TLS.*Use cURL: true/, + /Connection test result: success.*Auth type: TLS/, + ); + await expect(page).toMatchText( + "#lapi_ping_result", + /Use cURL: true/, ); // Good settings with curl @@ -313,7 +321,11 @@ describe(`Test TLS auth in Live mode`, () => { await expect(page).toMatchText( "#lapi_ping_result", - /Connection test result: success.*Auth type: TLS.*Use cURL: true/, + /Connection test result: success.*Auth type: TLS/, + ); + await expect(page).toMatchText( + "#lapi_ping_result", + /Use cURL: true/, ); // Good settings without curl @@ -326,7 +338,11 @@ describe(`Test TLS auth in Live mode`, () => { await expect(page).toMatchText( "#lapi_ping_result", - /Connection test result: success.*Auth type: TLS.*Use cURL: false/, + /Connection test result: success.*Auth type: TLS/, + ); + await expect(page).toMatchText( + "#lapi_ping_result", + /Use cURL: false/, ); await onAdminSaveSettings(); diff --git a/Test/EndToEnd/__tests__/6-events.js b/Test/EndToEnd/__tests__/6-events.js deleted file mode 100644 index fc2edde..0000000 --- a/Test/EndToEnd/__tests__/6-events.js +++ /dev/null @@ -1,375 +0,0 @@ -/* eslint-disable no-undef */ -const { - removeAllDecisions, - onLoginPageLoginAsAdmin, - goToAdmin, - goToPublicPage, - setDefaultConfig, - goToSettingsPage, - deleteFileContent, - getFileContent, - fillInput, - fillByName, - selectByName, - selectElement, - onAdminSaveSettings, - wait, - flushCache, -} = require("../utils/helpers"); - -const { CURRENT_IP, PROXY_IP, EVENT_LOG_PATH } = require("../utils/constants"); - -const CURRENT_TIME = `${Date.now()}`; -const FIRSTNAME = `E2EFirstname-${CURRENT_TIME}`; -const LASTNAME = `E2ELastname-${CURRENT_TIME}`; -const EMAIL = `${FIRSTNAME}${LASTNAME}@crowdsec.net`; -const PASSWORD = `PwD-${CURRENT_TIME}`; -const STREET = "Street"; -const CITY = "City"; -const POSTCODE = "12345"; -const PHONE = "0607080910"; - -describe(`Log events in front`, () => { - beforeAll(async () => { - await goToAdmin(); - await onLoginPageLoginAsAdmin(); - await removeAllDecisions(); - await setDefaultConfig(); - }); - - beforeEach(async () => { - await deleteFileContent(EVENT_LOG_PATH); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toBe(""); - }); - - it("Should create and login a customer", async () => { - await goToPublicPage("/customer/account/create"); - await fillInput("firstname", FIRSTNAME); - await fillInput("lastname", LASTNAME); - await fillInput("email_address", EMAIL); - await fillInput("password", PASSWORD); - await fillInput("password-confirmation", PASSWORD); - await page.click(".action.submit.primary"); - - await expect(page).toMatchText( - ".message-success", - /Thank you for registering/, - ); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toMatch( - new RegExp( - `{"type":"CUSTOMER_REGISTER_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ), - ); - await expect(logContent).toMatch( - new RegExp( - `{"type":"CUSTOMER_REGISTER_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ), - ); - await expect(logContent).toMatch( - `{"type":"CUSTOMER_LOGIN_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should Log out and failed registering with same email", async () => { - await page.click(".action.switch"); - await page.click('li.authorization-link:has-text("Sign Out")'); - - await goToPublicPage("/customer/account/create"); - - await fillInput("firstname", FIRSTNAME); - await fillInput("lastname", LASTNAME); - await fillInput("email_address", EMAIL); - await fillInput("password", PASSWORD); - await fillInput("password-confirmation", PASSWORD); - await page.click(".action.submit.primary"); - - await expect(page).toMatchText( - ".message-error", - /There is already an account with this email address./, - ); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toMatch( - `{"type":"CUSTOMER_REGISTER_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - await expect(logContent).not.toMatch( - `{"type":"CUSTOMER_REGISTER_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should not log register process if configuration disabled", async () => { - await goToSettingsPage(); - await selectElement( - "crowdsec_bouncer_events_log_customer_register", - "0", - ); - await onAdminSaveSettings(); - await goToPublicPage("/customer/account/create"); - - await fillInput("firstname", FIRSTNAME); - await fillInput("lastname", LASTNAME); - await fillInput("email_address", EMAIL); - await fillInput("password", PASSWORD); - await fillInput("password-confirmation", PASSWORD); - await page.click(".action.submit.primary"); - - await expect(page).toMatchText( - ".message-error", - /There is already an account with this email address./, - ); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).not.toMatch( - `{"type":"CUSTOMER_REGISTER_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should login ", async () => { - await page.click('li.authorization-link:has-text("Sign In")'); - - await fillInput("email", EMAIL); - await fillInput("pass", PASSWORD); - await page.click(".action.login.primary"); - await wait(2000); - - await expect(page).toMatchText(".welcome > span", /Welcome/); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toMatch( - `{"type":"CUSTOMER_LOGIN_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - await expect(logContent).toMatch( - `{"type":"CUSTOMER_LOGIN_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should not log login if configuration disabled", async () => { - await page.click(".action.switch"); - await page.click('li.authorization-link:has-text("Sign Out")'); - - await goToSettingsPage(); - await selectElement("crowdsec_bouncer_events_log_customer_login", "0"); - await onAdminSaveSettings(); - - await goToPublicPage("/customer/account/create"); - - await page.click('li.authorization-link:has-text("Sign In")'); - - await fillInput("email", EMAIL); - await fillInput("pass", PASSWORD); - await page.click(".action.login.primary"); - await wait(2000); - await expect(page).toMatchText(".welcome > span", /Welcome/); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).not.toMatch( - `{"type":"CUSTOMER_LOGIN_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - await expect(logContent).not.toMatch( - `{"type":"CUSTOMER_LOGIN_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should add to cart", async () => { - await goToPublicPage("/simple-product-10.html"); - await page.click("#product-addtocart-button"); - - await expect(page).toMatchText( - ".message-success", - /You added Simple Product 10/, - ); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toMatch( - `{"type":"ADD_TO_CART_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - await expect(logContent).toMatch( - `{"type":"ADD_TO_CART_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should not log add to cart if disabled configuration", async () => { - await goToSettingsPage(); - await selectElement("crowdsec_bouncer_events_log_add_to_cart", "0"); - await onAdminSaveSettings(); - - await goToPublicPage("/simple-product-10.html"); - await page.click("#product-addtocart-button"); - - await expect(page).toMatchText( - ".message-success", - /You added Simple Product 10/, - ); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).not.toMatch( - `{"type":"ADD_TO_CART_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - await expect(logContent).not.toMatch( - `{"type":"ADD_TO_CART_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should place order", async () => { - await goToPublicPage("/checkout"); - - await wait(2000); - await fillByName("city", CITY); - await fillByName("postcode", POSTCODE); - await fillByName("telephone", PHONE); - await selectByName("country_id", "FR"); - await fillByName("street\\[0\\]", STREET); - await page.click(".action.continue.primary"); - - await page.click(".action.primary.checkout"); - - await wait(2000); - await expect(page).toMatchTitle("Success Page"); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toMatch( - new RegExp( - `{"type":"PAYMENT_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"payment_method":"checkmo"`, - ), - ); - await expect(logContent).toMatch( - new RegExp( - `{"type":"PAYMENT_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"payment_method":"checkmo"`, - ), - ); - const element = await page.$(".order-number"); - - let incrementId = await element.innerText(); - incrementId = incrementId - .replace("", "") - .replace("", ""); - await expect(logContent).toMatch( - new RegExp( - `{"type":"ORDER_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"order_id":"${incrementId}","customer_id":".*","quote_id":".*"`, - ), - ); - await expect(logContent).toMatch( - new RegExp( - `{"type":"ORDER_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"order_id":"${incrementId}","customer_id":".*","quote_id":".*"`, - ), - ); - }); - - it("Should not log place order if configuration disabled", async () => { - await goToSettingsPage(); - await selectElement("crowdsec_bouncer_events_log_order", "0"); - await onAdminSaveSettings(); - - await goToPublicPage("/simple-product-10.html"); - await page.click("#product-addtocart-button"); - - await expect(page).toMatchText( - ".message-success", - /You added Simple Product 10/, - ); - - await goToPublicPage("/checkout"); - - await wait(2000); - await page.click(".action.continue.primary"); - - await page.click(".action.primary.checkout"); - - await wait(2000); - await expect(page).toMatchTitle("Success Page"); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).not.toMatch( - new RegExp( - `{"type":"PAYMENT_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"payment_method":"checkmo"`, - ), - ); - await expect(logContent).not.toMatch( - new RegExp( - `{"type":"PAYMENT_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"payment_method":"checkmo"`, - ), - ); - const element = await page.$(".order-number"); - - let incrementId = await element.innerText(); - incrementId = incrementId - .replace("", "") - .replace("", ""); - await expect(logContent).not.toMatch( - new RegExp( - `{"type":"ORDER_PROCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"order_id":"${incrementId}","customer_id":".*","quote_id":".*"`, - ), - ); - await expect(logContent).not.toMatch( - new RegExp( - `{"type":"ORDER_SUCCESS","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}",.*,"order_id":"${incrementId}","customer_id":".*","quote_id":".*"`, - ), - ); - }); -}); - -describe(`Log events in admin`, () => { - beforeEach(async () => { - await deleteFileContent(EVENT_LOG_PATH); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toBe(""); - }); - - it("Should log failed admin user login", async () => { - await goToAdmin("admin/auth/logout/"); - await expect(page).toMatchText( - ".message-success", - /You have logged out/, - ); - await page.fill("#username", "BAD_USER"); - await page.fill("#login", "BAD_PASSWORD"); - await page.click(".action-login"); - - await expect(page).toHaveSelector(".message-error"); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).toMatch( - `{"type":"ADMIN_LOGIN_FAILED","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should not log if main configuration is disabled", async () => { - await goToAdmin(); - await onLoginPageLoginAsAdmin(); - await goToSettingsPage(); - await selectElement("crowdsec_bouncer_events_log_enabled", "0"); - await onAdminSaveSettings(); - - await goToAdmin("admin/auth/logout/"); - await expect(page).toMatchText( - ".message-success", - /You have logged out/, - ); - await page.fill("#username", "BAD_USER"); - await page.fill("#login", "BAD_PASSWORD"); - await page.click(".action-login"); - - await expect(page).toHaveSelector(".message-error"); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).not.toMatch( - `{"type":"ADMIN_LOGIN_FAILED","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); - - it("Should not log failed admin user login if disabled configuration", async () => { - await goToAdmin(); - await onLoginPageLoginAsAdmin(); - await goToSettingsPage(); - await selectElement("crowdsec_bouncer_events_log_enabled", "1"); - await selectElement("crowdsec_bouncer_events_log_admin_login", "0"); - await onAdminSaveSettings(); - - await goToAdmin("admin/auth/logout/"); - await expect(page).toMatchText( - ".message-success", - /You have logged out/, - ); - await page.fill("#username", "BAD_USER"); - await page.fill("#login", "BAD_PASSWORD"); - await page.click(".action-login"); - - await expect(page).toHaveSelector(".message-error"); - const logContent = await getFileContent(EVENT_LOG_PATH); - await expect(logContent).not.toMatch( - `{"type":"ADMIN_LOGIN_FAILED","ip":"${PROXY_IP}","x-forwarded-for-ip":"${CURRENT_IP}"`, - ); - }); -}); diff --git a/Test/EndToEnd/utils/constants.js b/Test/EndToEnd/utils/constants.js index d728c6a..2a57f21 100644 --- a/Test/EndToEnd/utils/constants.js +++ b/Test/EndToEnd/utils/constants.js @@ -11,7 +11,6 @@ const { TIMEOUT } = process.env; const { CURRENT_IP } = process.env; const { PROXY_IP } = process.env; const DEBUG_LOG_PATH = `${__dirname}/../../../../../var/log/crowdsec-bouncer-debug.log`; -const EVENT_LOG_PATH = `${__dirname}/../../../../../var/log/crowdsec-events.log`; const JAPAN_IP = "210.249.74.42"; const FRANCE_IP = "78.119.253.85"; const { VAR_PATH, TLS_PATH } = process.env; @@ -28,7 +27,6 @@ module.exports = { ADMIN_LOGIN, ADMIN_PASSWORD, DEBUG_LOG_PATH, - EVENT_LOG_PATH, M2_URL, BOUNCER_KEY, CURRENT_IP, diff --git a/Test/EndToEnd/utils/helpers.js b/Test/EndToEnd/utils/helpers.js index 809872c..4e40a9f 100644 --- a/Test/EndToEnd/utils/helpers.js +++ b/Test/EndToEnd/utils/helpers.js @@ -53,18 +53,12 @@ const ensureConfigVisibilty = async () => { if (!visible) { await page.click("#crowdsec_bouncer_advanced-head"); } - visible = await page.isVisible("#crowdsec_bouncer_events"); - if (!visible) { - await page.click("#crowdsec_bouncer_events-head"); - } visible = await page.isVisible("#crowdsec_bouncer_general"); await expect(visible).toBeTruthy(); visible = await page.isVisible("#crowdsec_bouncer_theme"); await expect(visible).toBeTruthy(); visible = await page.isVisible("#crowdsec_bouncer_advanced"); await expect(visible).toBeTruthy(); - visible = await page.isVisible("#crowdsec_bouncer_events"); - await expect(visible).toBeTruthy(); }; const onAdminGoToSettingsPage = async (direct = true) => { @@ -173,13 +167,6 @@ const setDefaultConfig = async (save = true, direct = true) => { await selectElement("crowdsec_bouncer_advanced_debug_log", "1"); await selectElement("crowdsec_bouncer_advanced_debug_display_errors", "1"); await fillInput("crowdsec_bouncer_advanced_debug_forced_test_ip", ""); - // Events - await selectElement("crowdsec_bouncer_events_log_enabled", "1"); - await selectElement("crowdsec_bouncer_events_log_customer_register", "1"); - await selectElement("crowdsec_bouncer_events_log_customer_login", "1"); - await selectElement("crowdsec_bouncer_events_log_admin_login", "1"); - await selectElement("crowdsec_bouncer_events_log_add_to_cart", "1"); - await selectElement("crowdsec_bouncer_events_log_order", "1"); if (save) { await onAdminSaveSettings(); diff --git a/composer.json b/composer.json index af173fc..a811c3f 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "crowdsec/magento2-module-bouncer", "description": "The Bouncer module for Magento 2 allows rejecting IP detected as malicious by CrowdSec", "type": "magento2-module", - "version": "2.0.0", + "version": "2.1.0", "minimum-stability": "stable", "license": "MIT", "authors": [ @@ -28,9 +28,9 @@ ], "require": { "magento/framework": ">=102", - "crowdsec/bouncer": "^1.2.0", + "crowdsec/bouncer": "^2.1.0", "swaggest/json-schema": "0.12.39", - "crowdsec/magento-symfony-cache": "1.1.0 || 2.2.0" + "crowdsec/magento-symfony-cache": "1.1.0 || 2.2.0 || 3.0.0" }, "autoload": { "files": [ diff --git a/doc/TECHNICAL_NOTES.md b/doc/TECHNICAL_NOTES.md index 7c177cd..ddd1d9a 100644 --- a/doc/TECHNICAL_NOTES.md +++ b/doc/TECHNICAL_NOTES.md @@ -24,31 +24,6 @@ This extension is mainly based on the CrowdSec Bouncer PHP library. It is an ope [here](https://github.com/crowdsecurity/php-cs-bouncer). -## Events logging - -Events logging feature is essentially based on Magento 2 event and observer pattern. Please look at `etc/frontend/events.xml` and `etc/events.xml` files for more details. - -We are using the following events: - -- `backend_auth_user_login_failed` -- `checkout_cart_product_add_after` -- `checkout_cart_product_add_before` -- `customer_login` -- `customer_register_success` -- `sales_order_place_before` -- `sales_order_payment_place_end` -- `sales_order_payment_place_start` -- `sales_order_place_after` - -Additionally, as there is sometimes no available event, we use the Magento 2 plugin (interceptor) pattern. -Please look at `etc/frontend/di.xml` and `etc/di.xml` files for more details. - -We are using `before` plugins for the following methods: - -- `Magento\Customer\Model\AccountManagement::authenticate` -- `Magento\Customer\Controller\Account\CreatePost::execute` - - ## Full Page Cache In Magento 2, the full page cache is implemented via a plugin on the front controller (`vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php::aroundDispatch`). @@ -81,24 +56,30 @@ As Magento 2.2 is not compatible with PHP 7.2 until [2.2.9](https://github.com/m ## Why `crowdsec/magento-symfony-cache` ? -This `CrowdSec_Bouncer` module depends on the [CrowdSec PHP library `crowdsec/bouncer`](https://github.com/crowdsecurity/php-cs-bouncer) that comes with`symfony/cache` as dependency (`v5` or `v6`). +The [Magento 2 `CrowdSec_Bouncer` module](https://github.com/crowdsecurity/cs-magento-bouncer/) depends on the +[CrowdSec PHP library `crowdsec/bouncer`](https://github.com/crowdsecurity/php-cs-bouncer) that comes with +`symfony/cache` as dependency (`v5` or `v6`). +With Magento `2.4.4` and `2.4.5`, a fresh installation on PHP 8 will lock a `3.0.0` version of `psr/cache`. +And it will also install a `v2.2.11` version of `web-token/jwt-framework` that locks a `v4.4.45` version of +`symfony/http-kernel`. -Since Magento `2.4.4`, a fresh installation on PHP 8 will lock a `3.0.0` version of `psr/cache`. And it also installs a `v2.2.11` version of `web-token/jwt-framework` that locks a `v4.4.45` version of`symfony/http-kernel`. - -As a `v5` version of `symfony/cache` required `^1.0|^2.0` version of `psr/cache`, and a `v6` version of `symfony/cache` conflicts with `symfony/http-kernel` <5.4, it is impossible to require any version of the`symfony/cache` package. +As a `v5` version of `symfony/cache` required `^1.0|^2.0` version of `psr/cache`, and a `v6` version of +`symfony/cache` conflicts with `symfony/http-kernel` <5.4, it is impossible to require any version of the +`symfony/cache` package. That's why we needed to create a fork of `symfony/cache` that we called `crowdsec/magento-symfony-cache`. -The `v1` version of `crowdsec/magento-symfony-cache` only requires some specific `5.x.y` version of `symfony/cache`and is only available for PHP < `8.0.2`. - -For PHP >= `8.0.2`, we provide a compatible `v2` version of `crowdsec/magento-symfony-cache`. -This `v2` version replaces the specified `5.x.y` version of `symfony/cache` : we use a copy of `5.x.y` files and allow `psr/cache` `3.0`. We also copy some `6.0.z` files to have compatible PHP 8 method signatures. +The `v1` version of `crowdsec/magento-symfony-cache` only requires some specific `5.x.y` version of `symfony/cache` +and is only available for PHP < `8.0.2`. +For PHP >= `8.0.2`, we provide a compatible `v2` version of +`crowdsec/magento-symfony-cache`. +The `v2` version replaces the specified `5.x.y` version of `symfony/cache` : we use a copy of `5.x.y` files and +allow `psr/cache` `3.0`. We also copy some `6.0.z` files to have compatible PHP 8 method signatures. -_Update_: Since Magento `2.4.6`, it is possible to install `symfony/cache` because the required version of -`web-token/jwt-framework` is `3.1`. But, in order to keep compatibility with `2.4.4` and `2.4.5`, we have to -keep this `crowdsec/magento-symfony-cache` dependency. +Since Magento `2.4.6`, it is possible to install `symfony/cache` without this dependency hell. That's why we provide +an alternative `v3` version that is just a mirror of `symfony/cache`. Whenever it is possible, composer will pick up this version and just use the original `symfony/cache` package. diff --git a/doc/USER_GUIDE.md b/doc/USER_GUIDE.md index fb03cd8..8fe3d65 100644 --- a/doc/USER_GUIDE.md +++ b/doc/USER_GUIDE.md @@ -62,7 +62,8 @@ On the other hand, all texts are also fully customizable. This will allow you, f This module comes with configurations that you will find under `Stores → Configurations → Security → CrowdSec Bouncer` admin section. -These configurations are divided in four main parts : `General Settings`, `Theme customizations`, `Advanced settings` and `Events`. +These configurations are divided in three main parts : `General Settings`, `Theme customizations` and `Advanced +settings`. #### General Settings @@ -147,6 +148,21 @@ By default, `file_get_contents` method is used to call Local API. This method re Here, you can choose to use `cURL` requests instead. Beware that in this case, you need to have php `cURL` extension installed and enabled on your system. +*** + +`Connection details → Global timeout` (`global` scope) + +In seconds. The global timeout when calling Local API. If set to 0, timeout will be unlimited. + + +*** + + +`Connection details → Connection timeout` (`global` scope) + +In seconds. **Only for curl**. The timeout for the connection phase when calling Local API. If set to a 0, timeout will be unlimited. + + **N.B** : Even before saving configuration, you can check if your settings are correct by clicking on the test button. @@ -384,48 +400,6 @@ For test purpose only. This IP will be used instead of the current `X-Forwarded- *** -#### Events - - -In the `Events` part, you can enable business events logging. Using it in combination to a specific CrowdSec scenario allows detecting suspicious behavior as credential or credit card stuffing. - -![Events](images/screenshots/config-event.jpg) - -*** - -`Logging → Enable events log` (`global` scope) - -If enabled, logs will be written in a `var/log/crowdsec-events.log` file. - -*** - -`Logging → Track customer registration` (`global` scope) - -Will be used to detect suspicious account creation. - -*** - -`Logging → Track customer login` (`global` scope) - -Will be used to detect credential stuffing. - -*** - -`Logging → Track admin user login` (`global` scope) - -Will be used to detect admin brute attacks. - -*** - -`Logging → Track add to cart process` (`global` scope) - -Will be used to detect suspicious behaviour with add to cart action. - -*** - -`Logging → Track order process` (`global` scope) - -Will be used to detect suspicious behaviour, as credit card stuffing, on order action. ### Auto Prepend File mode diff --git a/doc/images/screenshots/config-connection-details.jpg b/doc/images/screenshots/config-connection-details.jpg index e09e4df..485c51e 100644 Binary files a/doc/images/screenshots/config-connection-details.jpg and b/doc/images/screenshots/config-connection-details.jpg differ diff --git a/etc/adminhtml/events.xml b/etc/adminhtml/events.xml deleted file mode 100755 index 8c6aa57..0000000 --- a/etc/adminhtml/events.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index da3fe4b..2ccfa43 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -53,10 +53,11 @@ CrowdSec\Bouncer\Model\Config\Source\ConnexionType - + cscli bouncers add magento2-bouncer]]> required-entry + Magento\Config\Model\Config\Backend\Encrypted api_key @@ -101,7 +102,20 @@ Magento\Config\Model\Config\Source\Yesno cURL instead of file_get_contents.]]> - + + + + required-entry validate-digits + + + + + + 1 + + required-entry validate-digits + + Test connection CrowdSec\Bouncer\Block\Adminhtml\System\Config\Connection\Ping @@ -490,62 +504,6 @@ - - - 1 - - - - - When enabled, you can choose which processes to track.]]> - 1 - - - Magento\Config\Model\Config\Source\Yesno - - - - - Magento\Config\Model\Config\Source\Yesno - - - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - - 1 - - - - diff --git a/etc/config.xml b/etc/config.xml index 3a49a88..16e433f 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -32,6 +32,9 @@ api_key + 60 + 10 + normal_bouncing @@ -81,15 +84,6 @@ 86400 - - - 1 - 1 - 1 - 1 - 1 - - diff --git a/etc/di.xml b/etc/di.xml index e43746d..474d85e 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -27,7 +27,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> @@ -42,25 +42,9 @@ - - - crowdsec-events - - CrowdSec\Bouncer\Logger\Handlers\Event - - - - - - - - - - - diff --git a/etc/events.xml b/etc/events.xml deleted file mode 100755 index a1ea96c..0000000 --- a/etc/events.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index 29e6f99..3856dc6 100755 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -27,7 +27,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> diff --git a/etc/frontend/events.xml b/etc/frontend/events.xml deleted file mode 100755 index fee5869..0000000 --- a/etc/frontend/events.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/view/adminhtml/templates/system/config/connection/old/ping.phtml b/view/adminhtml/templates/system/config/connection/old/ping.phtml index 8cf24e1..a7568f3 100644 --- a/view/adminhtml/templates/system/config/connection/old/ping.phtml +++ b/view/adminhtml/templates/system/config/connection/old/ping.phtml @@ -44,7 +44,9 @@ tls_key_path: $('escapeJs($block->getTlsKeyPathField()) ?>').value, tls_verify_peer: $('escapeJs($block->getTlsVerifyPeerField()) ?>').value, tls_ca_cert_path: $('escapeJs($block->getTlsCaCertPathField()) ?>').value, - use_curl: $('escapeJs($block->getUseCurlField()) ?>').value + use_curl: $('escapeJs($block->getUseCurlField()) ?>').value, + api_connect: $('escapeJs($block->getApiTimeoutField()) ?>').value, + api_connect_timeout: $('escapeJs($block->getApiConnectTimeoutField()) ?>').value }; new Ajax.Request('escapeJs($block->escapeUrl($block->getAjaxUrl())) ?>', { diff --git a/view/adminhtml/templates/system/config/connection/ping.phtml b/view/adminhtml/templates/system/config/connection/ping.phtml index 9259b8b..442252e 100644 --- a/view/adminhtml/templates/system/config/connection/ping.phtml +++ b/view/adminhtml/templates/system/config/connection/ping.phtml @@ -35,6 +35,8 @@ $ajaxUrl = $escaper->escapeJs($block->getAjaxUrl()); $errorMessage = $escaper->escapeJs($escaper->escapeHtml(__('Error during CrowdSec Connection ping.'))); $urlField = $escaper->escapeJs($block->getUrlField()); +$apiTimeoutField = $escaper->escapeJs($block->getApiTimeoutField()); +$apiConnectTimeoutField = $escaper->escapeJs($block->getApiConnectTimeoutField()); $authField = $escaper->escapeJs($block->getAuthTypeField()); $tlsCertField = $escaper->escapeJs($block->getTlsCertPathField()); $tlsKeyField = $escaper->escapeJs($block->getTlsKeyPathField()); @@ -58,7 +60,9 @@ require(['prototype'], function(){ tls_verify_peer: $('{$tlsVerifyPeerField}').value, tls_ca_cert_path: $('{$tlsCaCertField}').value, bouncer_key: $('{$keyField}').value, - use_curl: $('{$useCurlField}').value + use_curl: $('{$useCurlField}').value, + api_timeout: $('{$apiTimeoutField}').value, + api_connect_timeout: $('{$apiConnectTimeoutField}').value }; new Ajax.Request('{$ajaxUrl}', {