Skip to content

Commit

Permalink
Merge pull request codeigniter4#4727 from MGatner/publisher
Browse files Browse the repository at this point in the history
Publisher Class
  • Loading branch information
MGatner authored Jun 13, 2021
2 parents 3d0a4a7 + f33d880 commit 3b54e81
Show file tree
Hide file tree
Showing 16 changed files with 2,648 additions and 3 deletions.
28 changes: 28 additions & 0 deletions app/Config/Publisher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Config;

use CodeIgniter\Config\Publisher as BasePublisher;

/**
* Publisher Configuration
*
* Defines basic security restrictions for the Publisher class
* to prevent abuse by injecting malicious files into a project.
*/
class Publisher extends BasePublisher
{
/**
* A list of allowed destinations with a (pseudo-)regex
* of allowed files for each destination.
* Attempts to publish to directories not in this list will
* result in a PublisherException. Files that do no fit the
* pattern will cause copy/merge to fail.
*
* @var array<string,string>
*/
public $restrictions = [
ROOTPATH => '*',
FCPATH => '#\.(?css|js|map|htm?|xml|json|webmanifest|tff|eot|woff?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
];
}
113 changes: 113 additions & 0 deletions system/Commands/Utilities/Publish.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

/**
* This file is part of the CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CodeIgniter\Commands\Utilities;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use CodeIgniter\Publisher\Publisher;

/**
* Discovers all Publisher classes from the "Publishers/" directory
* across namespaces. Executes `publish()` from each instance, parsing
* each result.
*/
class Publish extends BaseCommand
{
/**
* The group the command is lumped under
* when listing commands.
*
* @var string
*/
protected $group = 'CodeIgniter';

/**
* The Command's name
*
* @var string
*/
protected $name = 'publish';

/**
* The Command's short description
*
* @var string
*/
protected $description = 'Discovers and executes all predefined Publisher classes.';

/**
* The Command's usage
*
* @var string
*/
protected $usage = 'publish [<directory>]';

/**
* The Command's arguments
*
* @var array<string, string>
*/
protected $arguments = [
'directory' => '[Optional] The directory to scan within each namespace. Default: "Publishers".',
];

/**
* the Command's Options
*
* @var array
*/
protected $options = [];

//--------------------------------------------------------------------

/**
* Displays the help for the spark cli script itself.
*
* @param array $params
*/
public function run(array $params)
{
$directory = array_shift($params) ?? 'Publishers';

if ([] === $publishers = Publisher::discover($directory))
{
CLI::write(lang('Publisher.publishMissing', [$directory]));
return;
}

foreach ($publishers as $publisher)
{
if ($publisher->publish())
{
CLI::write(lang('Publisher.publishSuccess', [
get_class($publisher),
count($publisher->getPublished()),
$publisher->getDestination(),
]), 'green');
}
else
{
CLI::error(lang('Publisher.publishFailure', [
get_class($publisher),
$publisher->getDestination(),
]), 'light_gray', 'red');

foreach ($publisher->getErrors() as $file => $exception)
{
CLI::write($file);
CLI::error($exception->getMessage());
CLI::newLine();
}
}
}
}
}
42 changes: 42 additions & 0 deletions system/Config/Publisher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/**
* This file is part of the CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CodeIgniter\Config;

/**
* Publisher Configuration
*
* Defines basic security restrictions for the Publisher class
* to prevent abuse by injecting malicious files into a project.
*/
class Publisher extends BaseConfig
{
/**
* A list of allowed destinations with a (pseudo-)regex
* of allowed files for each destination.
* Attempts to publish to directories not in this list will
* result in a PublisherException. Files that do no fit the
* pattern will cause copy/merge to fail.
*
* @var array<string,string>
*/
public $restrictions = [
ROOTPATH => '*',
FCPATH => '#\.(?css|js|map|htm?|xml|json|webmanifest|tff|eot|woff?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
];

/**
* Disables Registrars to prevent modules from altering the restrictions.
*/
final protected function registerProperties()
{
}
}
24 changes: 24 additions & 0 deletions system/Language/en/Publisher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* This file is part of the CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

// Publisher language settings
return [
'collision' => 'Publisher encountered an unexpected {0} while copying {1} to {2}.',
'expectedDirectory' => 'Publisher::{0} expects a valid directory.',
'expectedFile' => 'Publisher::{0} expects a valid file.',
'destinationNotAllowed' => 'Destination is not on the allowed list of Publisher directories: {0}',
'fileNotAllowed' => '{0} fails the following restriction for {1}: {2}',

// Publish Command
'publishMissing' => 'No Publisher classes detected in {0} across all namespaces.',
'publishSuccess' => '{0} published {1} file(s) to {2}.',
'publishFailure' => '{0} failed to publish to {1}!',
];
75 changes: 75 additions & 0 deletions system/Publisher/Exceptions/PublisherException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/**
* This file is part of the CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CodeIgniter\Publisher\Exceptions;

use CodeIgniter\Exceptions\FrameworkException;

/**
* Publisher Exception Class
*
* Handles exceptions related to actions taken by a Publisher.
*/
class PublisherException extends FrameworkException
{
/**
* Throws when a file should be overwritten yet cannot.
*
* @param string $from The source file
* @param string $to The destination file
*/
public static function forCollision(string $from, string $to)
{
return new static(lang('Publisher.collision', [filetype($to), $from, $to]));
}

/**
* Throws when an object is expected to be a directory but is not or is missing.
*
* @param string $caller The method causing the exception
*/
public static function forExpectedDirectory(string $caller)
{
return new static(lang('Publisher.expectedDirectory', [$caller]));
}

/**
* Throws when an object is expected to be a file but is not or is missing.
*
* @param string $caller The method causing the exception
*/
public static function forExpectedFile(string $caller)
{
return new static(lang('Publisher.expectedFile', [$caller]));
}

/**
* Throws when given a destination that is not in the list of allowed directories.
*
* @param string $destination
*/
public static function forDestinationNotAllowed(string $destination)
{
return new static(lang('Publisher.destinationNotAllowed', [$destination]));
}

/**
* Throws when a file fails to match the allowed pattern for its destination.
*
* @param string $file
* @param string $directory
* @param string $pattern
*/
public static function forFileNotAllowed(string $file, string $directory, string $pattern)
{
return new static(lang('Publisher.fileNotAllowed', [$file, $directory, $pattern]));
}
}
Loading

0 comments on commit 3b54e81

Please sign in to comment.