diff --git a/phpstan.neon b/phpstan.neon index 1b2c9bbe..8723f7dd 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3435,11 +3435,6 @@ parameters: count: 1 path: src/Twig/Configuration.php - - - message: "#^Parameter \\#1 \\$callback of function call_user_func expects callable\\(\\)\\: mixed, array\\{App\\\\Configuration\\\\SystemConfiguration, mixed\\} given\\.$#" - count: 1 - path: src/Twig/Configuration.php - - message: "#^Parameter \\#1 \\$string of function strtolower expects string, string\\|null given\\.$#" count: 2 diff --git a/src/Twig/Configuration.php b/src/Twig/Configuration.php index a00afdaa..a03dbc5d 100644 --- a/src/Twig/Configuration.php +++ b/src/Twig/Configuration.php @@ -11,23 +11,26 @@ namespace App\Twig; use App\Configuration\SystemConfiguration; use App\Constants; +use Twig\Environment; use Twig\Extension\AbstractExtension; +use Twig\Extension\SandboxExtension; +use Twig\Sandbox\SecurityError; use Twig\TwigFunction; final class Configuration extends AbstractExtension { - public function __construct(private SystemConfiguration $configuration) + public function __construct(private readonly SystemConfiguration $configuration) { } public function getFunctions(): array { return [ - new TwigFunction('config', [$this, 'get']), + new TwigFunction('config', $this->get(...), ['needs_environment' => true]), ]; } - public function get(string $name) + public function get(Environment $environment, string $name) { switch ($name) { case 'chart-class': @@ -42,6 +45,31 @@ final class Configuration extends AbstractExtension return '300'; case 'theme.calendar.background_color': return Constants::DEFAULT_COLOR; + case 'themeAllowAvatarUrls': + return $this->configuration->isThemeAllowAvatarUrls(); + // whitelisted configs that can be read even in invoice environments + case 'theme.branding.logo': + case 'theme.branding.company': + return $this->configuration->find($name); + } + + if (str_starts_with($name, 'saml.') || str_starts_with($name, 'ldap.')) { + throw new SecurityError(\sprintf('Templates cannot access security configuration %s.', $name)); + } + + if ($environment->hasExtension(SandboxExtension::class)) { + $sandbox = $environment->getExtension(SandboxExtension::class); + if ($sandbox->isSandboxed()) { + throw new SecurityError('Sandboxed template tried to access configuration key: ' . $name); + } + } + + $checks = ['is' . $name, 'get' . $name, 'has' . $name, $name]; + + foreach ($checks as $methodName) { + if (method_exists($this->configuration, $methodName)) { + return \call_user_func($this->configuration->$methodName(...)); + } } return $this->configuration->find($name); @@ -49,14 +77,8 @@ final class Configuration extends AbstractExtension public function __call($name, $arguments) { - $checks = ['is' . $name, 'get' . $name, 'has' . $name, $name]; + @trigger_error('Accessing "kimai_config" is deprecated and always return null, use config() instead', E_USER_DEPRECATED); - foreach ($checks as $methodName) { - if (method_exists($this->configuration, $methodName)) { - return \call_user_func([$this->configuration, $methodName], $arguments); - } - } - - return $this->configuration->find($name); + return null; } } diff --git a/src/Twig/Context.php b/src/Twig/Context.php index 3f8dbdf8..b7801ba7 100644 --- a/src/Twig/Context.php +++ b/src/Twig/Context.php @@ -9,12 +9,11 @@ namespace App\Twig; -use App\Configuration\SystemConfiguration; use Symfony\Component\HttpFoundation\RequestStack; final class Context { - public function __construct(private SystemConfiguration $systemConfiguration, private RequestStack $requestStack) + public function __construct(private readonly RequestStack $requestStack) { } @@ -58,8 +57,8 @@ final class Context public function getBranding(string $config): mixed { - @trigger_error('Use "kimai_config" instead of "kimai_context" to access system configurations', E_USER_DEPRECATED); + @trigger_error('Use config() instead of "kimai_context" to access system configurations', E_USER_DEPRECATED); - return $this->systemConfiguration->find('theme.branding.' . $config); + return null; } } diff --git a/templates/activity/details.html.twig b/templates/activity/details.html.twig index eb775b45..5efa33db 100644 --- a/templates/activity/details.html.twig +++ b/templates/activity/details.html.twig @@ -93,7 +93,7 @@ {% endif %} {% if stats is not null %} - {% set currency = kimai_config.customerDefaultCurrency %} + {% set currency = config('customerDefaultCurrency') %} {% if activity.project is not null %} {% set currency = activity.project.customer.currency %} {% endif %} diff --git a/templates/bundles/TablerBundle/components/avatar_image.html.twig b/templates/bundles/TablerBundle/components/avatar_image.html.twig index 9fa50f56..6c7639a5 100644 --- a/templates/bundles/TablerBundle/components/avatar_image.html.twig +++ b/templates/bundles/TablerBundle/components/avatar_image.html.twig @@ -1,5 +1,5 @@ {% macro avatar_image(user) %} - {% if user.avatar is not empty and kimai_config.themeAllowAvatarUrls %} + {% if user.avatar is not empty and config('themeAllowAvatarUrls') %} {% set avatar = asset(user.avatar, 'avatars') %}   {% else %} diff --git a/templates/macros/widgets.html.twig b/templates/macros/widgets.html.twig index 36b372ae..41d43030 100644 --- a/templates/macros/widgets.html.twig +++ b/templates/macros/widgets.html.twig @@ -122,7 +122,7 @@ {% macro user_avatar(user, tooltip, class, badge) %} {% set avatar = null %} - {% if user.avatar is not empty and kimai_config.themeAllowAvatarUrls %} + {% if user.avatar is not empty and config('themeAllowAvatarUrls') %} {% set avatar = asset(user.avatar, 'avatars') %} {% endif %} {% if not user.enabled %} diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig index 38cc7938..d0aaf87e 100644 --- a/templates/security/login.html.twig +++ b/templates/security/login.html.twig @@ -48,12 +48,12 @@ {% block login_social_auth %} {% if saml_config.isActivated() %} - {% if kimai_config.loginFormActive %} + {% if config('loginFormActive') %}
{{ 'or'|trans({}, 'TablerBundle') }}
{% endif %}
- {% if not kimai_config.loginFormActive %} + {% if not config('loginFormActive') %}

{{ block('login_box_msg') }}

{% endif %}
@@ -75,25 +75,25 @@ {% endblock %} {% block login_box %} - {% if kimai_config.loginFormActive %} + {% if config('loginFormActive') %} {{ parent() }} {% endif %} {% endblock %} {% block login_form %} - {% if kimai_config.loginFormActive %} + {% if config('loginFormActive') %} {{ parent() }} {% endif %} {% endblock %} {% block password_forgotten %} - {% if kimai_config.passwordResetActive %} + {% if config('passwordResetActive') %} {{ parent() }} {% endif %} {% endblock %} {% block registration %} - {% if kimai_config.selfRegistrationActive %} + {% if config('selfRegistrationActive') %} {{ parent() }} {% endif %} {% endblock %} diff --git a/templates/security/unlock.html.twig b/templates/security/unlock.html.twig index 55bda61a..c7e1fdd3 100644 --- a/templates/security/unlock.html.twig +++ b/templates/security/unlock.html.twig @@ -19,7 +19,7 @@ {% endif %}
{% block unlock_form %} - {% if kimai_config.loginFormActive %} + {% if config('loginFormActive') %}