diff --git a/_config/oembed.yml b/_config/oembed.yml index 31a1b742b..091babc55 100644 --- a/_config/oembed.yml +++ b/_config/oembed.yml @@ -4,6 +4,11 @@ Name: coreoembed SilverStripe\Core\Injector\Injector: SilverStripe\View\Embed\Embeddable: class: SilverStripe\View\Embed\EmbedContainer + Psr\Http\Client\ClientInterface.oembed: + class: GuzzleHttp\Client + Embed\Http\Crawler: + constructor: + - '%$Psr\Http\Client\ClientInterface.oembed' Embed\Embed: constructor: - '%$Embed\Http\Crawler' diff --git a/composer.json b/composer.json index 08806cc7a..0bb28858c 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,8 @@ }, "conflict": { "egulias/email-validator": "^2", - "phpunit/phpunit": "^6 || ^7 || ^8" + "phpunit/phpunit": "^6 || ^7 || ^8", + "cwp/cwp-core": "<2.11.0" }, "provide": { "psr/container-implementation": "1.0.0" diff --git a/docs/en/00_Getting_Started/03_Environment_Management.md b/docs/en/00_Getting_Started/03_Environment_Management.md index 869ed26e0..aa87a727d 100644 --- a/docs/en/00_Getting_Started/03_Environment_Management.md +++ b/docs/en/00_Getting_Started/03_Environment_Management.md @@ -58,13 +58,16 @@ Environment::setEnv('API_KEY', 'AABBCCDDEEFF012345'); ### Using environment variables in config -To use environment variables in `.yaml` configs you can reference them using backticks. +To use environment variables in `.yaml` configs you can reference them using backticks. You can have multiple +environment variables within a single value, though the overall value must start and end with backticks. ```yaml SilverStripe\Core\Injector\Injector: MyServiceClass: properties: - MyProperty: '`ENV_VAR_HERE`' + MyProperty: '`ENV_VAR_ONE`' + MultiValueProperty: '`ENV_VAR_ONE`:`ENV_VAR_TWO`' + ThisWillNotSubstitute: 'lorem `REGULAR_TEXT` ipsum' ``` [info] diff --git a/docs/en/02_Developer_Guides/03_Forms/Field_types/03_HTMLEditorField.md b/docs/en/02_Developer_Guides/03_Forms/Field_types/03_HTMLEditorField.md index afae5c3e6..373da7403 100644 --- a/docs/en/02_Developer_Guides/03_Forms/Field_types/03_HTMLEditorField.md +++ b/docs/en/02_Developer_Guides/03_Forms/Field_types/03_HTMLEditorField.md @@ -265,6 +265,19 @@ SilverStripe\AssetAdmin\Forms\RemoteFileFormFactory: HtmlEditorConfig::get('cms')->disablePlugins('ssembed'); ``` +Use the following config if you need to send outbound requests through a proxy: + +```yaml +--- +Name: myembed +After: coreoembed +--- +SilverStripe\Core\Injector\Injector: + Psr\Http\Client\ClientInterface.oembed: + constructor: + - proxy: '111.222.333.444:55' +``` + ## Limiting oembed URLs HtmlEditorField can have whitelists set on both the scheme (default http & https) and domains allowed when diff --git a/docs/en/04_Changelogs/4.11.0.md b/docs/en/04_Changelogs/4.11.0.md index ce6ad0c0a..e30bc606f 100644 --- a/docs/en/04_Changelogs/4.11.0.md +++ b/docs/en/04_Changelogs/4.11.0.md @@ -9,6 +9,7 @@ - [Preview any DataObject in any admin section](#cms-preview) - [Meta generator tag now shows framework version number](#meta-tag-version) - [Allow-plugins configuration option in Composer versions 2.2.0 and up](#composer) + - [Users will recieve an email if their password is changed](#change-password-email) - [Other features](#other-features) - [Bugfixes](#bugfixes) - [Dependency and internal API changes](#dependency-internal-api-changes) @@ -91,6 +92,21 @@ SilverStripe\CMS\Model\SiteTree: New installations using `silverstripe/silverstripe-installer` and `silverstripe/recipe-kitchen-sink` from 4.11 onwards will have the above plugins added to the `allowed-plugins` configuration by default. - From July 2022 composer will no longer prompt to allow plugins when running `composer install`. This won't affect new installs using silverstripe/installer or silverstripe-recipe-kitchen-sink, but will affect other new projects, and existing projects where `allowed-plugins` hasn't yet been defined. In those cases developers will need to declare the allowed plugins manually in the project's `composer.json` file. +### Users will recieve an email if their password is changed {#change-password-email} + +The `SilverStripe\Security\Member.notify_password_change` configuration has been set to `true` by default - it used to be `false`. This means when a user changes their password on a project in "live" mode, they will recieve an email alerting them that their password was changed. The email includes a link to change their password again, so that users can recover their account in the event that someone else changed their password without their knowledge. + +This change was made to improve the default security of your projects, but if you do not want this behaviour you can disable it by setting the configuration to false: + +```yml +SilverStripe\Security\Member: + notify_password_change: false +``` + +The email content can also be changed by overriding the `SilverStripe\Control\Email\ChangePasswordEmail` template. + +Note that this configuration is already enabled by default in the `cwp/cwp-core` module. Projects which have that as a dependency won't experience any change in behaviour. + ### Other new features {#other-features} - A new [AbstractGridFieldComponent](https://api.silverstripe.org/4/SilverStripe/Forms/GridField/AbstractGridFieldComponent.html) class has been added to make it easier to globally add fundamental functionality to `GridFieldComponent`s. All classes packaged with the Silverstripe framework which implement the `GridFieldComponent` interface are subclasses of the new abstract class, making them all `Injectable`. Maintainers of third-party packages which include classes that implement `GridFieldComponent` are encouraged to subclass the `AbstractGridFieldComponent` abstract class. @@ -104,6 +120,7 @@ This release includes a number of bug fixes to improve a broad range of areas. C - If `guzzlehttp/guzzle` is required, it must now be at least `7.3.0`. This was done to ensure that v2 of `guzzlehttp/psr7` is installed, which is used by `embed/embed` v4 - `embed/embed` has been upgraded from v3 to v4. The internal implementation of the internal `Embeddable` interface has been changed from `EmbedResource` to `EmbedContainer` +- `embed/embed` has been configured to use a guzzle client instead of the default curl client so that a proxy configuration value can be set if required diff --git a/lang/en.yml b/lang/en.yml index 7edee6a42..8b35ea48f 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -5,7 +5,7 @@ en: EDITINFO: 'Edit this file' REMOVE: Remove SilverStripe\Control\ChangePasswordEmail_ss: - CHANGEPASSWORDFOREMAIL: 'The password for account with email address {email} has been changed. If you didn\''t change your password please change your password using the link below' + CHANGEPASSWORDFOREMAIL: 'The password for account with email address {email} has been changed. If you didn''t change your password please change your password using the link below' CHANGEPASSWORDTEXT1: 'You changed your password for' CHANGEPASSWORDTEXT3: 'Change password' HELLO: Hi diff --git a/src/Core/Injector/Injector.php b/src/Core/Injector/Injector.php index 98da89a2c..f5b4b87c8 100644 --- a/src/Core/Injector/Injector.php +++ b/src/Core/Injector/Injector.php @@ -524,16 +524,31 @@ class Injector implements ContainerInterface } // Evaluate constants surrounded by back ticks - if (preg_match('/^`(?[^`]+)`$/', $value ?? '', $matches)) { - $envValue = Environment::getEnv($matches['name']); - if ($envValue !== false) { - $value = $envValue; - } elseif (defined($matches['name'] ?? '')) { - $value = constant($matches['name'] ?? ''); - } else { - $value = null; + $hasBacticks = false; + $allMissing = true; + // $value must start and end with backticks, though there can be multiple + // things being subsituted within $value e.g. "`VAR_ONE`:`VAR_TWO`:`VAR_THREE`" + if (preg_match('/^`.+`$/', $value ?? '')) { + $hasBacticks = true; + preg_match_all('/`(?[^`]+)`/', $value, $matches); + foreach ($matches['name'] as $name) { + $envValue = Environment::getEnv($name); + $val = ''; + if ($envValue !== false) { + $val = $envValue; + } elseif (defined($name)) { + $val = constant($name); + } + $value = str_replace("`$name`", $val, $value); + if ($val) { + $allMissing = false; + } } } + // silverstripe sometimes explictly expects a null value rather than just an empty string + if ($hasBacticks && $allMissing && $value === '') { + return null; + } return $value; } diff --git a/src/Security/Member.php b/src/Security/Member.php index 82a80c768..7f06edf13 100644 --- a/src/Security/Member.php +++ b/src/Security/Member.php @@ -106,7 +106,7 @@ class Member extends DataObject * @config * @var boolean */ - private static $notify_password_change = false; + private static $notify_password_change = true; /** * All searchable database columns diff --git a/tests/php/Core/Injector/InjectorTest.php b/tests/php/Core/Injector/InjectorTest.php index e9b8b6db4..3b8cd1850 100644 --- a/tests/php/Core/Injector/InjectorTest.php +++ b/tests/php/Core/Injector/InjectorTest.php @@ -3,6 +3,7 @@ namespace SilverStripe\Core\Tests\Injector; use SilverStripe\Core\Config\Config; +use SilverStripe\Core\Environment; use SilverStripe\Core\Injector\Factory; use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\InjectorNotFoundException; @@ -815,6 +816,32 @@ class InjectorTest extends SapphireTest $this->assertInstanceOf(OtherTestObject::class, $item->property->property); } + /** + * @dataProvider provideConvertServicePropertyBackTicks + */ + public function testConvertServicePropertyBackTicks($value, $expected) + { + Environment::setEnv('INJECTOR_TEST_CSP_A', 'ABC'); + Environment::setEnv('INJECTOR_TEST_CSP_B', 'DEF'); + Environment::setEnv('INJECTOR_TEST_CSP_C', 'GHI'); + $actual = Injector::inst()->convertServiceProperty($value); + $this->assertSame($expected, $actual); + } + + public function provideConvertServicePropertyBackTicks() + { + return [ + ['`INJECTOR_TEST_CSP_A`', 'ABC'], + ['`INJECTOR_TEST_CSP_A`:`INJECTOR_TEST_CSP_B`', 'ABC:DEF'], + ['`INJECTOR_TEST_CSP_A` some text `INJECTOR_TEST_CSP_B`', 'ABC some text DEF'], + ['`INJECTOR_TEST_CSP_A``INJECTOR_TEST_CSP_B`', 'ABCDEF'], + ['`INJECTOR_TEST_CSP_A`:`INJECTOR_TEST_CSP_B``INJECTOR_TEST_CSP_C`', 'ABC:DEFGHI'], + ['`INJECTOR_TEST_CSP_A`:`INJECTOR_TEST_CSP_X`', 'ABC:'], + ['`INJECTOR_TEST_CSP_X`', null], + ['lorem `INJECTOR_TEST_CSP_A` ipsum', 'lorem `INJECTOR_TEST_CSP_A` ipsum'], + ]; + } + public function testNamedServices() { $injector = new Injector();