Skip to content

Commit

Permalink
Craft::parseEnv(), etc.
Browse files Browse the repository at this point in the history
Resolves #3219
  • Loading branch information
brandonkelly committed Oct 23, 2018
1 parent 93db58e commit 237d696
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 56 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG-v3.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Added the Project Config, a portable and centralized configuration for system settings. ([#1429](https://github.com/craftcms/cms/issues/1429))
- Elements, field layouts, sites, and site groups are now soft-deleted. ([#867](https://github.com/craftcms/cms/issues/867))
- Entries, categories, and users can now be restored within the Control Panel by searching for `is:trashed` and clicking the “Restore” button.
- Some Site settings (Base URL), volume settings (Base URL and File System Path), and email settings (System Email Address, Sender Name, HTML Email Template, Username, Password, and Host Name) can now be set to environment variables using a `$VARIABLE_NAME` syntax. ([#3219](https://github.com/craftcms/cms/issues/3219))
- Added cross-domain support for Live Preview. ([#1521](https://github.com/craftcms/cms/issues/1521))
- Custom fields can now opt out of being included in elements’ search keywords. ([#2600](https://github.com/craftcms/cms/issues/2600))
- Added the `disableAdminFunctions` config setting.
Expand All @@ -12,6 +13,8 @@
- Added the `gc` console command, which can be used to run garbage collection tasks.
- Added the `trashed` element query param, which can be used to query for elements that have been soft-deleted.
- Added the `expression()` Twig function, for creating new `yii\db\Expression` objects in templates. ([#3289](https://github.com/craftcms/cms/pull/3289))
- Added the `parseEnv()` Twig function.
- Added `Craft::parseEnv()`.
- Added `craft\base\ApplicationTrait::getIsLive()`.
- Added `craft\base\Element::EVENT_AFTER_RESTORE`.
- Added `craft\base\Element::EVENT_BEFORE_RESTORE`.
Expand All @@ -21,6 +24,7 @@
- Added `craft\base\Field::EVENT_BEFORE_ELEMENT_RESTORE`.
- Added `craft\base\FieldInterface::afterElementRestore()`.
- Added `craft\base\FieldInterface::beforeElementRestore()`.
- Added `craft\behaviors\EnvAttributeParserBehavior`.
- Added `craft\controllers\LivePreviewController`.
- Added `craft\db\Command::restore()`.
- Added `craft\db\Command::softDelete()`.
Expand All @@ -39,6 +43,7 @@
- Added `craft\helpers\ProjectConfig`.
- Added `craft\models\FieldLayout::createFromConfig()`.
- Added `craft\models\FieldLayout::getConfig()`.
- Added `craft\models\Site::getBaseUrl()`.
- Added `craft\services\Categories::getGroupByUid()`.
- Added `craft\services\Elements::restoreElement()`.
- Added `craft\services\Elements::EVENT_AFTER_RESTORE_ELEMENT`.
Expand Down Expand Up @@ -74,6 +79,7 @@
- Deprecated `craft\models\Info::getOn()`. `Craft::$app->getIsLive()` should be used instead.
- Deprecated `craft\models\Info::getTimezone()`. `Craft::$app->getTimeZone()` should be used instead.
- Deprecated `craft\services\SystemSettings`. `craft\services\ProjectConfig` should be used instead.
- Deprecated `craft\validators\UrlValidator::$allowAlias`. `craft\behaviors\EnvAttributeParserBehavior` should be used instead.

### Security
- It’s no longer possible to spoof Live Preview requests.
28 changes: 28 additions & 0 deletions src/Craft.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,34 @@ class Craft extends Yii
// Public Methods
// =========================================================================

/**
* Checks if a string references an environment variable (`$VARIABLE_NAME`)
* and/or an alias (`@aliasName`), and returns the referenced value.
*
* ---
*
* ```php
* $value1 = Craft::parseEnv('$SMPT_PASSWORD');
* $value2 = Craft::parseEnv('@webroot');
* ```
*
* @param string $str
* @return string The parsed value, or the original value if it didn’t
* reference an environment variable and/or alias.
*/
public static function parseEnv(string $str): string
{
if (preg_match('/^\$(\w+)$/', $str, $matches)) {
$str = getenv($matches[1]) ?: $str;
}

if (isset($str[0]) && $str[0] === '@') {
$str = static::getAlias($str) ?: $str;
}

return $str;
}

/**
* Displays a variable.
*
Expand Down
2 changes: 1 addition & 1 deletion src/base/Volume.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,6 @@ public function getRootUrl()
return false;
}

return rtrim(Craft::getAlias($this->url), '/') . '/';
return rtrim(Craft::parseEnv($this->url), '/') . '/';
}
}
102 changes: 102 additions & 0 deletions src/behaviors/EnvAttributeParserBehavior.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/

namespace craft\behaviors;

use Craft;
use craft\base\FieldInterface;
use craft\helpers\StringHelper;
use craft\models\FieldLayout;
use yii\base\Behavior;
use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\validators\UrlValidator;

/**
* EnvAttributeParserBehavior can be applied to models with attributes that can be
* set to either environment variables (`$VARIABLE_NAME`) or aliases (`@aliasName`)`.
*
* ---
*
* ```php
* public function attributes()
* {
* return [
* 'parser' => [
* 'class' => EnvAttributeParserBehavior::class,
* 'attributes' => ['attr1', 'attr2', '...'],
* ],
* ];
* }
* ```
*
* @author Pixel & Tonic, Inc. <[email protected]>
* @since 3.1
*/
class EnvAttributeParserBehavior extends Behavior
{
/**
* @var Model
*/
public $owner;

/**
* @var string[] The attributes names that can be set to environment
* variables (`$VARIABLE_NAME`) and/or aliases (`@aliasName`).
*/
public $attributes = [];

/**
* @var array Keeps track of the original attribute values
*/
private $_values;

/**
* @inheritdoc
*/
public function events()
{
return [
Model::EVENT_BEFORE_VALIDATE => 'beforeValidate',
Model::EVENT_AFTER_VALIDATE => 'afterValidate',
];
}

/**
* Replaces attribute values before validation occurs.
*/
public function beforeValidate()
{
$this->_values = [];

foreach ($this->attributes as $attribute) {
$value = $this->owner->$attribute;
if (($parsed = Craft::parseEnv($value)) !== $value) {
$this->_values[$attribute] = $value;
$this->owner->$attribute = $parsed;

foreach ($this->owner->getActiveValidators($attribute) as $validator) {
if ($validator instanceof UrlValidator) {
$validator->defaultScheme = null;
}

$validator->message = StringHelper::ensureRight($validator->message, ' ({value})');
}
}
}
}

/**
* Restores the original attribute values after validation occurs.
*/
public function afterValidate()
{
foreach ($this->_values as $attribute => $value) {
$this->owner->$attribute = $value;
}
}
}
6 changes: 4 additions & 2 deletions src/helpers/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,10 @@ public static function mailerConfig(MailSettings $settings = null): array
return [
'class' => Mailer::class,
'messageClass' => Message::class,
'from' => [$settings->fromEmail => $settings->fromName],
'template' => $settings->template,
'from' => [
Craft::parseEnv($settings->fromEmail) => Craft::parseEnv($settings->fromName)
],
'template' => Craft::parseEnv($settings->template),
'transport' => $adapter->defineTransport(),
];
}
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/UrlHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,8 @@ public static function baseSiteUrl(): string
{
try {
$currentSite = Craft::$app->getSites()->getCurrentSite();
if ($currentSite->baseUrl) {
return rtrim(Craft::getAlias($currentSite->baseUrl), '/') . '/';
if (($baseUrl = $currentSite->getBaseUrl()) !== null) {
return $baseUrl;
}
} catch (SiteNotFoundException $e) {
// Fail silently if Craft isn't installed yet or is in the middle of updating
Expand Down
4 changes: 2 additions & 2 deletions src/mail/Mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ public function send($message)
$settings = App::mailSettings();
$variables = ($message->variables ?: []) + [
'emailKey' => $message->key,
'fromEmail' => $settings->fromEmail,
'fromName' => $settings->fromName,
'fromEmail' => Craft::parseEnv($settings->fromEmail),
'fromName' => Craft::parseEnv($settings->fromName),
];

// Render the subject and textBody
Expand Down
29 changes: 13 additions & 16 deletions src/mail/transportadapters/Gmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
namespace craft\mail\transportadapters;

use Craft;
use craft\helpers\StringHelper;
use yii\base\Exception;
use craft\behaviors\EnvAttributeParserBehavior;

/**
* Smtp implements a Gmail transport adapter into Craft’s mailer.
Expand Down Expand Up @@ -54,19 +53,17 @@ public static function displayName(): string
/**
* @inheritdoc
*/
public function init()
public function behaviors()
{
parent::init();

if ($this->password) {
try {
$this->password = StringHelper::decdec($this->password);
} catch (Exception $e) {
Craft::error('Could not decode Gmail password: ' . $e->getMessage());
Craft::$app->getErrorHandler()->logException($e);
$this->password = null;
}
}
return [
'parser' => [
'class' => EnvAttributeParserBehavior::class,
'attributes' => [
'username',
'password',
],
]
];
}

/**
Expand Down Expand Up @@ -112,8 +109,8 @@ public function defineTransport()
'host' => 'smtp.gmail.com',
'port' => 465,
'encryption' => 'ssl',
'username' => $this->username,
'password' => $this->password,
'username' => Craft::parseEnv($this->username),
'password' => Craft::parseEnv($this->password),
'timeout' => $this->timeout,
];
}
Expand Down
32 changes: 15 additions & 17 deletions src/mail/transportadapters/Smtp.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
namespace craft\mail\transportadapters;

use Craft;
use craft\helpers\StringHelper;
use yii\base\Exception;
use craft\behaviors\EnvAttributeParserBehavior;

/**
* Smtp implements a SMTP transport adapter into Craft’s mailer.
Expand Down Expand Up @@ -74,19 +73,18 @@ public static function displayName(): string
/**
* @inheritdoc
*/
public function init()
public function behaviors()
{
parent::init();

if ($this->password) {
try {
$this->password = StringHelper::decdec($this->password);
} catch (Exception $e) {
Craft::error('Could not decode SMTP password: ' . $e->getMessage());
Craft::$app->getErrorHandler()->logException($e);
$this->password = null;
}
}
return [
'parser' => [
'class' => EnvAttributeParserBehavior::class,
'attributes' => [
'host',
'username',
'password',
],
]
];
}

/**
Expand Down Expand Up @@ -142,14 +140,14 @@ public function defineTransport()
{
$config = [
'class' => \Swift_SmtpTransport::class,
'host' => $this->host,
'host' => Craft::parseEnv($this->host),
'port' => $this->port,
'timeout' => $this->timeout,
];

if ($this->useAuthentication) {
$config['username'] = $this->username;
$config['password'] = $this->password;
$config['username'] = Craft::parseEnv($this->username);
$config['password'] = Craft::parseEnv($this->password);
}

if ($this->encryptionMethod) {
Expand Down
18 changes: 18 additions & 0 deletions src/models/MailSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Craft;
use craft\base\Model;
use craft\behaviors\EnvAttributeParserBehavior;

/**
* MailSettings Model class.
Expand Down Expand Up @@ -49,6 +50,23 @@ class MailSettings extends Model
// Public Methods
// =========================================================================

/**
* @inheritdoc
*/
public function behaviors()
{
return [
'parser' => [
'class' => EnvAttributeParserBehavior::class,
'attributes' => [
'fromEmail',
'fromName',
'template',
],
]
];
}

/**
* @inheritdoc
*/
Expand Down
Loading

0 comments on commit 237d696

Please sign in to comment.