ActionController.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * MINZ - Copyright 2011 Marien Fressinaud
  5. * Sous licence AGPL3 <http://www.gnu.org/licenses/>
  6. */
  7. /**
  8. * The Minz_ActionController class is a controller in the MVC paradigm
  9. */
  10. abstract class Minz_ActionController {
  11. /** @var array<string,string> */
  12. private static array $csp_default = [
  13. 'default-src' => "'self'",
  14. 'frame-ancestors' => "'none'",
  15. ];
  16. /** @var array<string,string> */
  17. private array $csp_policies;
  18. /** @var Minz_View */
  19. protected $view;
  20. /**
  21. * Gives the possibility to override the default view model type.
  22. * @var class-string
  23. * @deprecated Use constructor with view type instead
  24. * @access private
  25. * @internal
  26. */
  27. public static string $defaultViewType = Minz_View::class;
  28. /**
  29. * @phpstan-param class-string|'' $viewType
  30. * @param string $viewType Name of the class (inheriting from Minz_View) to use for the view model
  31. */
  32. public function __construct(string $viewType = '') {
  33. $this->csp_policies = self::$csp_default;
  34. $view = null;
  35. if ($viewType !== '' && class_exists($viewType)) {
  36. $view = new $viewType();
  37. if (!($view instanceof Minz_View)) {
  38. $view = null;
  39. }
  40. }
  41. if ($view === null && class_exists(self::$defaultViewType)) { /// @phpstan-ignore staticProperty.deprecated
  42. $view = new self::$defaultViewType(); // @phpstan-ignore staticProperty.deprecated
  43. if (!($view instanceof Minz_View)) {
  44. $view = null;
  45. }
  46. }
  47. $this->view = $view ?? new Minz_View();
  48. $view_path = Minz_Request::controllerName() . '/' . Minz_Request::actionName() . '.phtml';
  49. $this->view->_path($view_path);
  50. $this->view->attributeParams();
  51. }
  52. /**
  53. * Getteur
  54. */
  55. public function view(): Minz_View {
  56. return $this->view;
  57. }
  58. /**
  59. * Set default CSP policies.
  60. * @param array<string,string> $policies An array where keys are directives and values are sources.
  61. */
  62. public static function _defaultCsp(array $policies): void {
  63. if (!isset($policies['default-src']) || !isset($policies['frame-ancestors'])) {
  64. Minz_Log::warning('Default CSP policy is not declared', ADMIN_LOG);
  65. }
  66. self::$csp_default = $policies;
  67. }
  68. /**
  69. * Set CSP policies.
  70. *
  71. * default-src and frame-ancestors directives should always be given.
  72. *
  73. * References:
  74. * - https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP
  75. * - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/default-src
  76. * - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors
  77. *
  78. * @param array<string,string> $policies An array where keys are directives and values are sources.
  79. */
  80. protected function _csp(array $policies): void {
  81. if (!isset($policies['default-src']) || !isset($policies['frame-ancestors'])) {
  82. $action = Minz_Request::controllerName() . '#' . Minz_Request::actionName();
  83. Minz_Log::warning(
  84. "Default CSP policy is not declared for action {$action}.",
  85. ADMIN_LOG
  86. );
  87. }
  88. $this->csp_policies = $policies;
  89. }
  90. /**
  91. * Send HTTP Content-Security-Policy header based on declared policies.
  92. */
  93. public function declareCspHeader(): void {
  94. $policies = [];
  95. foreach (Minz_ExtensionManager::listExtensions(true) as $extension) {
  96. $extension->amendCsp($this->csp_policies);
  97. }
  98. foreach ($this->csp_policies as $directive => $sources) {
  99. $policies[] = $directive . ' ' . $sources;
  100. }
  101. header('Content-Security-Policy: ' . implode('; ', $policies));
  102. }
  103. /**
  104. * Méthodes à redéfinir (ou non) par héritage
  105. * firstAction est la première méthode exécutée par le Dispatcher
  106. * lastAction est la dernière
  107. */
  108. public function init(): void { }
  109. public function firstAction(): void { }
  110. public function lastAction(): void { }
  111. }