forked from pkp/pkp-lib
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pkp#9895 app key manager and command added
- Loading branch information
1 parent
85fc9a7
commit 7a88e9d
Showing
11 changed files
with
599 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
namespace PKP\cliTool; | ||
|
||
use Illuminate\Console\Concerns\InteractsWithIO; | ||
use Illuminate\Console\OutputStyle; | ||
use Symfony\Component\Console\Input\StringInput; | ||
use Symfony\Component\Console\Output\StreamOutput; | ||
|
||
class CommandInterface | ||
{ | ||
use InteractsWithIO; | ||
|
||
public function __construct() | ||
{ | ||
$output = new OutputStyle( | ||
new StringInput(''), | ||
new StreamOutput(fopen('php://stdout', 'w')) | ||
); | ||
|
||
$this->setOutput($output); | ||
} | ||
|
||
public function errorBlock(array $messages = [], ?string $title = null): void | ||
{ | ||
$this->getOutput()->block( | ||
$messages, | ||
$title, | ||
'fg=white;bg=red', | ||
' ', | ||
true | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<?php | ||
|
||
/** | ||
* @file classes/cliTool/traits/HasCommandInterface.php | ||
* | ||
* Copyright (c) 2014-2024 Simon Fraser University | ||
* Copyright (c) 2000-2024 John Willinsky | ||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. | ||
* | ||
* @trait HasCommandInterface | ||
* | ||
* @ingroup tools | ||
* | ||
* @brief A helper trait for CLI tools that provide functionality to read/write on CLI interface | ||
*/ | ||
|
||
namespace PKP\cliTool\traits; | ||
|
||
use PKP\cliTool\CommandInterface; | ||
use Symfony\Component\Console\Helper\Helper; | ||
|
||
trait HasCommandInterface | ||
{ | ||
/** | ||
* CLI interface, this object should extends InteractsWithIO | ||
*/ | ||
protected ?CommandInterface $commandInterface = null; | ||
|
||
/** | ||
* Set the command interface | ||
*/ | ||
public function setCommandInterface(?CommandInterface $commandInterface = null): self | ||
{ | ||
$this->commandInterface = $commandInterface ?? new CommandInterface; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Get the command interface | ||
*/ | ||
public function getCommandInterface(): CommandInterface | ||
{ | ||
return $this->commandInterface; | ||
} | ||
|
||
/** | ||
* Print given options in a pretty way. | ||
*/ | ||
protected function printCommandList(array $options, bool $shouldTranslate = true): void | ||
{ | ||
$width = (int)collect(array_keys($options)) | ||
->map(fn($command) => Helper::width($command)) | ||
->sort() | ||
->last() + 2; | ||
|
||
foreach ($options as $commandName => $description) { | ||
$spacingWidth = $width - Helper::width($commandName); | ||
$this->getCommandInterface()->line( | ||
sprintf( | ||
' <info>%s</info>%s%s', | ||
$commandName, | ||
str_repeat(' ', $spacingWidth), | ||
$shouldTranslate ? __($description) : $description | ||
) | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
|
||
/** | ||
* @file classes/cliTool/traits/HasParameterList.php | ||
* | ||
* Copyright (c) 2014-2024 Simon Fraser University | ||
* Copyright (c) 2000-2024 John Willinsky | ||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. | ||
* | ||
* @trait HasParameterList | ||
* | ||
* @ingroup tools | ||
* | ||
* @brief A helper trait manage the params and flags on CLI interface | ||
*/ | ||
|
||
namespace PKP\cliTool\traits; | ||
|
||
trait HasParameterList | ||
{ | ||
/** | ||
* Parameters and arguments from CLI | ||
*/ | ||
protected ?array $parameterList = null; | ||
|
||
/** | ||
* Save the parameter list passed on CLI | ||
* | ||
* @param array $items Array with parameters and arguments passed on CLI | ||
*/ | ||
public function setParameterList(array $items): static | ||
{ | ||
$parameters = []; | ||
|
||
foreach ($items as $param) { | ||
if (strpos($param, '=')) { | ||
[$key, $value] = explode('=', ltrim($param, '-')); | ||
$parameters[$key] = $value; | ||
|
||
continue; | ||
} | ||
|
||
$parameters[] = $param; | ||
} | ||
|
||
$this->parameterList = $parameters; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Get the parameter list passed on CLI | ||
*/ | ||
public function getParameterList(): ?array | ||
{ | ||
return $this->parameterList; | ||
} | ||
|
||
/** | ||
* Get the value of a specific parameter | ||
*/ | ||
protected function getParameterValue(string $parameter, mixed $default = null): mixed | ||
{ | ||
if (!isset($this->getParameterList()[$parameter])) { | ||
return $default; | ||
} | ||
|
||
return $this->getParameterList()[$parameter]; | ||
} | ||
|
||
/** | ||
* Determined if the given flag set on CLI | ||
*/ | ||
protected function hasFlagSet(string $flag): bool | ||
{ | ||
return in_array($flag, $this->getParameterList()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
<?php | ||
|
||
/** | ||
* @file classes/core/PKPAppKey.php | ||
* | ||
* Copyright (c) 2014-2024 Simon Fraser University | ||
* Copyright (c) 2000-2024 John Willinsky | ||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. | ||
* | ||
* @class PKPAppKey | ||
* | ||
* @ingroup core | ||
* | ||
* @brief Class to manage app key related behaviours | ||
*/ | ||
|
||
namespace PKP\core; | ||
|
||
use Exception; | ||
use PKP\config\Config; | ||
use Illuminate\Support\Str; | ||
use PKP\config\ConfigParser; | ||
use Illuminate\Encryption\Encrypter; | ||
|
||
class PKPAppKey | ||
{ | ||
/** | ||
* The supported cipher algorithms and their properties. | ||
* | ||
* @var array | ||
*/ | ||
private static $supportedCiphers = [ | ||
'aes-128-cbc' => ['size' => 16, 'aead' => false], | ||
'aes-256-cbc' => ['size' => 32, 'aead' => false], | ||
'aes-128-gcm' => ['size' => 16, 'aead' => true], | ||
'aes-256-gcm' => ['size' => 32, 'aead' => true], | ||
]; | ||
|
||
/** | ||
* The default cipher algorithms | ||
* | ||
* @var string | ||
*/ | ||
private static $defaultCipher = 'aes-256-cbc'; | ||
|
||
/** | ||
* Get the list of supported ciphers | ||
*/ | ||
public static function getSupportedCiphers(): array | ||
{ | ||
return self::$supportedCiphers; | ||
} | ||
|
||
/** | ||
* Get the defined cipher | ||
*/ | ||
public static function getCipher(): string | ||
{ | ||
return Config::getVar('security', 'cipher', self::$defaultCipher); | ||
} | ||
|
||
/** | ||
* Has the app key defined in config file | ||
*/ | ||
public static function hasKey(): bool | ||
{ | ||
return !empty(Config::getVar('general', 'app_key', '')); | ||
} | ||
|
||
/** | ||
* Has the app key variable defined in config file | ||
*/ | ||
public static function hasKeyVariable(): bool | ||
{ | ||
return Config::hasVar('general', 'app_key'); | ||
} | ||
|
||
/** | ||
* Get the app key defined in config file | ||
*/ | ||
public static function getKey(): string | ||
{ | ||
return Config::getVar('general', 'app_key', ''); | ||
} | ||
|
||
/** | ||
* Validate a given cipher | ||
*/ | ||
public static function validateCipher(string $cipher): string | ||
{ | ||
$cipher = strtolower($cipher); | ||
|
||
if (!in_array($cipher, array_keys(static::getSupportedCiphers()))) { | ||
$ciphers = implode(', ', array_keys(static::getSupportedCiphers())); | ||
|
||
throw new Exception( | ||
sprintf( | ||
'Invalid cipher %s provided, must be among [%s]', | ||
$cipher, | ||
$ciphers | ||
) | ||
); | ||
} | ||
|
||
return $cipher; | ||
} | ||
|
||
/** | ||
* Validate given or config defined app | ||
*/ | ||
public static function validate(string $key = null, string $cipher = null): bool | ||
{ | ||
$config = app('config')->get('app'); | ||
|
||
return Encrypter::supported( | ||
static::parseKey($key ?? $config['key']), | ||
static::validateCipher($cipher ?? $config['cipher']) | ||
); | ||
} | ||
|
||
/** | ||
* Generate a new app key | ||
*/ | ||
public static function generate(string $cipher = null): string | ||
{ | ||
$config = app('config')->get('app'); | ||
|
||
return 'base64:'.base64_encode( | ||
Encrypter::generateKey(static::validateCipher($cipher ?? $config['cipher'])) | ||
); | ||
} | ||
|
||
/** | ||
* Write the given app key in the config file | ||
*/ | ||
public static function writeToConfig(string $key): bool | ||
{ | ||
if (!static::validate($key)) { | ||
$ciphers = implode(', ', array_keys(static::getSupportedCiphers())); | ||
|
||
// Error invalid app key | ||
throw new Exception( | ||
"Unsupported cipher or incorrect key length. Supported ciphers are: {$ciphers}." | ||
); | ||
} | ||
|
||
$configParser = new ConfigParser; | ||
$configParams = [ | ||
'general' => [ | ||
'app_key' => $key, | ||
], | ||
]; | ||
|
||
if (!static::hasKeyVariable()) { | ||
// Error if the config key `app_key` not defined under `general` section | ||
throw new Exception('Config variable named `app_key` not defined in the `general` section'); | ||
} | ||
|
||
if (!$configParser->updateConfig(Config::getConfigFileName(), $configParams)) { | ||
// Error reading config file | ||
throw new Exception('Unable to read the config file'); | ||
} | ||
|
||
if (!$configParser->writeConfig(Config::getConfigFileName())) { | ||
// Error writing config file | ||
throw new Exception('Unable to write the app key in the config file'); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Parse the given app key and return the real key value | ||
*/ | ||
public static function parseKey(string $key): string | ||
{ | ||
if (Str::startsWith($key, $prefix = 'base64:')) { | ||
$key = base64_decode(Str::after($key, $prefix)); | ||
} | ||
|
||
return $key; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.