Skip to content

Commit

Permalink
pkp#9895 app key manager and command added
Browse files Browse the repository at this point in the history
  • Loading branch information
touhidurabir committed May 2, 2024
1 parent 85fc9a7 commit 7a88e9d
Show file tree
Hide file tree
Showing 11 changed files with 599 additions and 163 deletions.
34 changes: 34 additions & 0 deletions classes/cliTool/CommandInterface.php
@@ -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
);
}
}
69 changes: 69 additions & 0 deletions classes/cliTool/traits/HasCommandInterface.php
@@ -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
)
);
}
}
}
78 changes: 78 additions & 0 deletions classes/cliTool/traits/HasParameterList.php
@@ -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());
}
}
183 changes: 183 additions & 0 deletions classes/core/PKPAppKey.php
@@ -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;
}
}
16 changes: 0 additions & 16 deletions classes/core/PKPApplication.php
Expand Up @@ -389,22 +389,6 @@ public static function getName()
return 'pkp-lib';
}

/**
* Get the default cipher algorithm
* Available and Valid cipher algorithms are
* - aes-128-cbc
* - aes-256-cbc
* - aes-128-gcm
* - aes-256-gcm
* @see \Illuminate\Encryption\Encrypter::$supportedCiphers
*
* @return string
*/
public static function getDefaultCipher(): string
{
return 'AES-256-CBC';
}

/**
* Get the locale key for the name of this application.
*
Expand Down

0 comments on commit 7a88e9d

Please sign in to comment.