diff --git a/app/config/eccube/services.yaml b/app/config/eccube/services.yaml
index ae666313f51..2d3d6c41338 100644
--- a/app/config/eccube/services.yaml
+++ b/app/config/eccube/services.yaml
@@ -58,6 +58,10 @@ services:
lazy: true
public: true
+ Eccube\Service\EnvFileService:
+ arguments:
+ $projectDir: '%kernel.project_dir%'
+
Eccube\Service\Composer\ComposerProcessService:
lazy: true
diff --git a/src/Eccube/Controller/Admin/Setting/System/SecurityController.php b/src/Eccube/Controller/Admin/Setting/System/SecurityController.php
index 9e34fdbd1bd..4caece0aace 100644
--- a/src/Eccube/Controller/Admin/Setting/System/SecurityController.php
+++ b/src/Eccube/Controller/Admin/Setting/System/SecurityController.php
@@ -15,6 +15,7 @@
use Eccube\Controller\AbstractController;
use Eccube\Form\Type\Admin\SecurityType;
+use Eccube\Service\EnvFileService;
use Eccube\Util\CacheUtil;
use Eccube\Util\StringUtil;
use Symfony\Bridge\Twig\Attribute\Template;
@@ -25,11 +26,27 @@
class SecurityController extends AbstractController
{
+ /**
+ * この画面が .env へ書き込む環境変数キー(OS 環境変数によるオーバーライド判定に使用).
+ */
+ private const ENV_KEYS = [
+ 'ECCUBE_FRONT_ALLOW_HOSTS',
+ 'ECCUBE_FRONT_DENY_HOSTS',
+ 'ECCUBE_ADMIN_ALLOW_HOSTS',
+ 'ECCUBE_ADMIN_DENY_HOSTS',
+ 'ECCUBE_FORCE_SSL',
+ 'TRUSTED_HOSTS',
+ 'ECCUBE_ADMIN_ROUTE',
+ ];
+
/**
* SecurityController constructor.
*/
- public function __construct(protected TokenStorageInterface $tokenStorage, private readonly CacheUtil $cacheUtil)
- {
+ public function __construct(
+ protected TokenStorageInterface $tokenStorage,
+ private readonly CacheUtil $cacheUtil,
+ private readonly EnvFileService $envFileService,
+ ) {
}
/**
@@ -44,13 +61,20 @@ public function index(Request $request): RedirectResponse|array
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
- // .envファイルが存在しないときに設定は失敗する
- if (file_exists($this->getParameter('kernel.project_dir').'/.env') === false) {
+ // .env ファイル自体が読み込まれない状況(全キー反映不可)では保存を拒否する
+ $ineffectiveReasons = $this->envFileService->getIneffectiveReasons();
+ if ([] !== $ineffectiveReasons) {
$this->addError('admin.common.save_error', 'admin');
+ foreach ($ineffectiveReasons as $reason) {
+ $this->addError('admin.system.env.ineffective.'.$reason, 'admin');
+ }
return $this->redirectToRoute('admin_setting_system_security');
}
+ // OS 環境変数やカスケードファイルで上書きされているキーは書き込んでも反映されないため除外する
+ $overriddenKeys = $this->envFileService->getOverriddenKeys(self::ENV_KEYS);
+
$data = $form->getData();
$envFile = $this->getParameter('kernel.project_dir').'/.env';
$env = file_get_contents($envFile);
@@ -69,20 +93,29 @@ public function index(Request $request): RedirectResponse|array
array_filter(\explode("\n", StringUtil::convertLineFeed($data['admin_deny_hosts'])), fn ($str) => StringUtil::isNotBlank($str))
);
- $env = StringUtil::replaceOrAddEnv($env, [
+ // 上書きされているキーは書き込み対象から除外する
+ $replace = array_diff_key([
'ECCUBE_FRONT_ALLOW_HOSTS' => "'{$frontAllowHosts}'",
'ECCUBE_FRONT_DENY_HOSTS' => "'{$frontDenyHosts}'",
'ECCUBE_ADMIN_ALLOW_HOSTS' => "'{$adminAllowHosts}'",
'ECCUBE_ADMIN_DENY_HOSTS' => "'{$adminDenyHosts}'",
'ECCUBE_FORCE_SSL' => $data['force_ssl'] ? '1' : '0',
'TRUSTED_HOSTS' => $data['trusted_hosts'],
- ]);
+ ], array_flip($overriddenKeys));
- file_put_contents($envFile, $env);
+ if ([] !== $replace) {
+ $env = StringUtil::replaceOrAddEnv($env, $replace);
+ file_put_contents($envFile, $env);
+ }
+
+ // 上書きされ反映されなかったキーは名指しで警告する
+ if ([] !== $overriddenKeys) {
+ $this->addWarning(trans('admin.system.env.ineffective.overridden', ['%keys%' => implode(', ', $overriddenKeys)]), 'admin');
+ }
- // 管理画面URLの更新. 変更されている場合はログアウトし再ログインさせる.
+ // 管理画面URLの更新. 上書きされておらず変更されている場合はログアウトし再ログインさせる.
$adminRoute = $this->eccubeConfig['eccube_admin_route'];
- if ($adminRoute !== $data['admin_route_dir']) {
+ if ($adminRoute !== $data['admin_route_dir'] && !in_array('ECCUBE_ADMIN_ROUTE', $overriddenKeys, true)) {
$env = StringUtil::replaceOrAddEnv($env, [
'ECCUBE_ADMIN_ROUTE' => $data['admin_route_dir'],
]);
@@ -115,13 +148,21 @@ public function index(Request $request): RedirectResponse|array
$this->addWarning('admin.setting.system.security.admin_url_warning', 'admin');
}
- // .envファイルが存在しない場合警告を出す。
- if (file_exists($this->getParameter('kernel.project_dir').'/.env') === false) {
- $this->addWarning('admin.setting.system.security.not_found_env_file', 'admin');
+ // .env ファイル自体が読み込まれない状況では警告を出し、登録ボタンを無効化する。
+ $ineffectiveReasons = $this->envFileService->getIneffectiveReasons();
+ foreach ($ineffectiveReasons as $reason) {
+ $this->addWarning('admin.system.env.ineffective.'.$reason, 'admin');
+ }
+
+ // 個々のキーが OS 環境変数等で上書きされている場合は該当キーを名指しで警告する。
+ $overriddenKeys = $this->envFileService->getOverriddenKeys(self::ENV_KEYS);
+ if ([] !== $overriddenKeys) {
+ $this->addWarning(trans('admin.system.env.ineffective.overridden', ['%keys%' => implode(', ', $overriddenKeys)]), 'admin');
}
return [
'form' => $form->createView(),
+ 'envWritable' => [] === $ineffectiveReasons,
];
}
}
diff --git a/src/Eccube/Controller/Admin/Store/TemplateController.php b/src/Eccube/Controller/Admin/Store/TemplateController.php
index cca6c9be39e..fc3a987eb08 100644
--- a/src/Eccube/Controller/Admin/Store/TemplateController.php
+++ b/src/Eccube/Controller/Admin/Store/TemplateController.php
@@ -18,6 +18,7 @@
use Eccube\Form\Type\Admin\TemplateType;
use Eccube\Repository\Master\DeviceTypeRepository;
use Eccube\Repository\TemplateRepository;
+use Eccube\Service\EnvFileService;
use Eccube\Util\CacheUtil;
use Eccube\Util\StringUtil;
use Symfony\Bridge\Twig\Attribute\Template;
@@ -33,10 +34,15 @@
class TemplateController extends AbstractController
{
+ /**
+ * この画面が .env へ書き込む環境変数キー(OS 環境変数によるオーバーライド判定に使用).
+ */
+ private const ENV_KEYS = ['ECCUBE_TEMPLATE_CODE'];
+
/**
* TemplateController constructor.
*/
- public function __construct(protected TemplateRepository $templateRepository, protected DeviceTypeRepository $deviceTypeRepository, private readonly CacheUtil $cacheUtil)
+ public function __construct(protected TemplateRepository $templateRepository, protected DeviceTypeRepository $deviceTypeRepository, private readonly CacheUtil $cacheUtil, private readonly EnvFileService $envFileService)
{
}
@@ -59,10 +65,28 @@ public function index(Request $request): array|RedirectResponse
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
+ // .env への書き込みが反映されない状況では保存を拒否する
+ // - .env が存在しない / 書き込み不可
+ // - .env.local.php(dump-env の最適化済みスナップショット)が .env より優先される
+ // - ECCUBE_TEMPLATE_CODE が OS 環境変数やカスケードファイルで .env を上書きしている
+ $ineffectiveReasons = $this->envFileService->getIneffectiveReasons();
+ $overriddenKeys = $this->envFileService->getOverriddenKeys(self::ENV_KEYS);
+ if ([] !== $ineffectiveReasons || [] !== $overriddenKeys) {
+ $this->addError('admin.common.save_error', 'admin');
+ foreach ($ineffectiveReasons as $reason) {
+ $this->addError('admin.system.env.ineffective.'.$reason, 'admin');
+ }
+ if ([] !== $overriddenKeys) {
+ $this->addError(trans('admin.system.env.ineffective.overridden', ['%keys%' => implode(', ', $overriddenKeys)]), 'admin');
+ }
+
+ return $this->redirectToRoute('admin_store_template');
+ }
+
$Template = $this->templateRepository->find($form['selected']->getData());
$envFile = $this->getParameter('kernel.project_dir').'/.env';
- $env = file_exists($envFile) ? file_get_contents($envFile) : '';
+ $env = file_get_contents($envFile);
$env = StringUtil::replaceOrAddEnv($env, [
'ECCUBE_TEMPLATE_CODE' => $Template->getCode(),
@@ -72,25 +96,28 @@ public function index(Request $request): array|RedirectResponse
$this->addSuccess('admin.common.save_complete', 'admin');
- // 次のいずれかの場合、.env への書き込みが起動時のロードで反映されない:
- // 1. ECCUBE_TEMPLATE_CODE がプロセス環境変数として設定されている(Docker などで明示的に設定した場合)。
- // bootEnv は OS 環境変数を上書きしないため .env の値が使われない。
- // 2. .env.local.php(dump-env の最適化済みスナップショット)が存在する。
- // bootEnv は .env より .env.local.php を優先するため、再度 `composer symfony:dump-env` を
- // 実行するまで .env の変更が反映されない。
- $envLocalPhp = $this->getParameter('kernel.project_dir').'/.env.local.php';
- if (false !== getenv('ECCUBE_TEMPLATE_CODE') || file_exists($envLocalPhp)) {
- $this->addWarning('admin.store.template.env_override_warning', 'admin');
- }
-
$this->cacheUtil->clearCache();
return $this->redirectToRoute('admin_store_template');
}
+ // .env への書き込みが反映されない状況では警告を出し、登録ボタンを無効化する。
+ $ineffectiveReasons = $this->envFileService->getIneffectiveReasons();
+ foreach ($ineffectiveReasons as $reason) {
+ $this->addWarning('admin.system.env.ineffective.'.$reason, 'admin');
+ }
+
+ // 対象キー(ECCUBE_TEMPLATE_CODE)が上書きされている場合は名指しで警告する。
+ $overriddenKeys = $this->envFileService->getOverriddenKeys(self::ENV_KEYS);
+ if ([] !== $overriddenKeys) {
+ $this->addWarning(trans('admin.system.env.ineffective.overridden', ['%keys%' => implode(', ', $overriddenKeys)]), 'admin');
+ }
+
return [
'form' => $form->createView(),
'Templates' => $Templates,
+ // この画面が扱うキーは 1 つのみのため, 上書きされていれば実質全滅として無効化する。
+ 'envWritable' => [] === $ineffectiveReasons && [] === $overriddenKeys,
];
}
@@ -98,7 +125,7 @@ public function index(Request $request): array|RedirectResponse
* テンプレート一覧からのダウンロード
*/
#[Route(path: '/%eccube_admin_route%/store/template/{id}/download', name: 'admin_store_template_download', requirements: ['id' => '\d+'], methods: ['GET'])]
- public function download(Request $request, \Eccube\Entity\Template $Template): BinaryFileResponse
+ public function download(\Eccube\Entity\Template $Template): BinaryFileResponse
{
// 該当テンプレートのディレクトリ
$templateCode = $Template->getCode();
@@ -155,7 +182,7 @@ public function download(Request $request, \Eccube\Entity\Template $Template): B
}
#[Route(path: '/%eccube_admin_route%/store/template/{id}/delete', name: 'admin_store_template_delete', requirements: ['id' => '\d+'], methods: ['DELETE'])]
- public function delete(Request $request, \Eccube\Entity\Template $Template): RedirectResponse
+ public function delete(\Eccube\Entity\Template $Template): RedirectResponse
{
$this->isTokenValid();
diff --git a/src/Eccube/Resource/locale/messages.en.yaml b/src/Eccube/Resource/locale/messages.en.yaml
index 04ede3d0514..acd8d8cf27a 100644
--- a/src/Eccube/Resource/locale/messages.en.yaml
+++ b/src/Eccube/Resource/locale/messages.en.yaml
@@ -1428,7 +1428,10 @@ admin.setting.system.security.force_ssl_description: Only https access is allowe
admin.setting.system.security.ip_limit_invalid_ipv4: "%ip% is not an IPv4 address."
admin.setting.system.security.ip_limit_invalid_https: "http is not allowed to do this setting."
admin.setting.system.security.admin_url_warning: Please set the Admin Console URL that is hard to guess for security.
-admin.setting.system.security.not_found_env_file: .env file not found. If you do not use the .env file you can not change security config in Admin Console.
+admin.system.env.ineffective.not_found: 'The .env file does not exist, so this setting cannot be changed from the admin screen. Please set the environment variables on the server directly.'
+admin.system.env.ineffective.not_writable: 'The .env file is not writable, so this setting cannot be changed from the admin screen. Please check the write permission of the .env file.'
+admin.system.env.ineffective.local_php: 'Because .env.local.php exists, changes to .env are not applied and cannot be made from the admin screen. Update .env on the server and re-run composer dump-env, or remove .env.local.php to re-enable changes from the admin screen.'
+admin.system.env.ineffective.overridden: 'The environment variable(s) for this setting (%keys%) are overridden by OS process environment variables or a cascade file (.env.local, etc.), so changes to .env are not applied. Please change the environment variable on the server directly.'
admin.setting.system.security.trusted_hosts: Trusted hosts
admin.setting.system.security.trusted_hosts_sample: ^example\.com$,^example\.org$
admin.setting.system.security.trusted_hosts_description: |
@@ -1515,7 +1518,6 @@ admin.store.template.delete_error__current_template: You are not allowed to dele
admin.store.template.template_code_already_exists: This template code is already used.
admin.store.template.upload_failed: Failed to upload
admin.store.template.invalid_upload_file: Wrong extension of the file
-admin.store.template.env_override_warning: "ECCUBE_TEMPLATE_CODE is set as an environment variable, so changes to .env will not take effect. To switch templates, update the ECCUBE_TEMPLATE_CODE environment variable."
admin.store.plugin: Plugins
admin.store.plugin.plugin_owners_install: Search Plugins
admin.store.plugin.plugin_list: All Plugins
diff --git a/src/Eccube/Resource/locale/messages.ja.yaml b/src/Eccube/Resource/locale/messages.ja.yaml
index 3dbaeedf0bb..10445399ff9 100644
--- a/src/Eccube/Resource/locale/messages.ja.yaml
+++ b/src/Eccube/Resource/locale/messages.ja.yaml
@@ -1428,7 +1428,10 @@ admin.setting.system.security.ip_limit_invalid_ipv4: "%ip%はIPv4アドレスで
admin.setting.system.security.ip_limit_invalid_ip_and_submask: "%ip%はIPv4/ビットマスクの形式ではありません。"
admin.setting.system.security.ip_limit_invalid_https: "httpの場合には設定できません。"
admin.setting.system.security.admin_url_warning: 管理画面URLは、セキュリティのため推測されにくいものを設定してください。
-admin.setting.system.security.not_found_env_file: .envファイルが見つかりません。.envを利用していない場合はセキュリティ設定を管理画面から変更できません。
+admin.system.env.ineffective.not_found: '.env ファイルが存在しないため、この設定は管理画面から変更できません。サーバーの環境変数を直接設定してください。'
+admin.system.env.ineffective.not_writable: '.env ファイルに書き込み権限がないため、この設定は管理画面から変更できません。.env ファイルの書き込み権限を確認してください。'
+admin.system.env.ineffective.local_php: '.env.local.php が存在するため、.env への変更は反映されず、管理画面からは変更できません。サーバー側で .env を更新してから composer dump-env を再実行するか、.env.local.php を削除して管理画面からの変更を再開してください。'
+admin.system.env.ineffective.overridden: 'この設定の環境変数(%keys%)が OS のプロセス環境変数またはカスケードファイル(.env.local 等)で上書きされているため、.env への変更は反映されません。サーバーの環境変数を直接変更してください。'
admin.setting.system.security.trusted_hosts: 信頼できるホスト名
admin.setting.system.security.trusted_hosts_sample: ^example\.com$,^example\.org$
admin.setting.system.security.trusted_hosts_description: |
@@ -1515,7 +1518,6 @@ admin.store.template.delete_error__current_template: 設定中のテンプレー
admin.store.template.template_code_already_exists: テンプレートコードは既に利用されています
admin.store.template.upload_failed: アップロードに失敗しました
admin.store.template.invalid_upload_file: ファイルの拡張子に誤りがあります
-admin.store.template.env_override_warning: "ECCUBE_TEMPLATE_CODE が環境変数として設定されているため、.env への変更は反映されません。テンプレートを切り替えるには環境変数の ECCUBE_TEMPLATE_CODE を変更してください。"
admin.store.plugin: プラグイン
admin.store.plugin.plugin_owners_install: プラグインを探す
admin.store.plugin.plugin_list: プラグイン一覧
diff --git a/src/Eccube/Resource/template/admin/Setting/System/security.twig b/src/Eccube/Resource/template/admin/Setting/System/security.twig
index 5afaf986d69..4b78931e948 100644
--- a/src/Eccube/Resource/template/admin/Setting/System/security.twig
+++ b/src/Eccube/Resource/template/admin/Setting/System/security.twig
@@ -219,7 +219,7 @@ file that was distributed with this source code.
-
+
diff --git a/src/Eccube/Resource/template/admin/Store/template.twig b/src/Eccube/Resource/template/admin/Store/template.twig
index 7d3adf5541f..642e61e4006 100644
--- a/src/Eccube/Resource/template/admin/Store/template.twig
+++ b/src/Eccube/Resource/template/admin/Store/template.twig
@@ -120,7 +120,7 @@ file that was distributed with this source code.