Merge branch 'develop' of github.com:cerebrate-project/cerebrate into develop
commit
25b6db7303
|
@ -13,11 +13,12 @@
|
|||
"cakephp/migrations": "^3.0",
|
||||
"cakephp/plugin-installer": "^1.2",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"mobiledetect/mobiledetectlib": "^2.8"
|
||||
"mobiledetect/mobiledetectlib": "^2.8",
|
||||
"pear/crypt_gpg": "^1.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/bake": "^2.0.3",
|
||||
"cakephp/cakephp-codesniffer": "~4.0.0",
|
||||
"cakephp/cakephp-codesniffer": "^4.0",
|
||||
"cakephp/debug_kit": "^4.0",
|
||||
"cebe/php-openapi": "^1.6",
|
||||
"fzaninotto/faker": "^1.9",
|
||||
|
@ -68,7 +69,11 @@
|
|||
},
|
||||
"prefer-stable": true,
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"cakephp/plugin-installer": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ class EncryptionKeysController extends AppController
|
|||
|
||||
public function index()
|
||||
{
|
||||
$this->EncryptionKeys->initializeGpg();
|
||||
$Model = $this->EncryptionKeys;
|
||||
$this->CRUD->index([
|
||||
'quickFilters' => $this->quickFilterFields,
|
||||
'filters' => $this->filterFields,
|
||||
|
@ -31,6 +33,20 @@ class EncryptionKeysController extends AppController
|
|||
],
|
||||
'contain' => $this->containFields,
|
||||
'statisticsFields' => $this->statisticsFields,
|
||||
'afterFind' => function($data) use ($Model) {
|
||||
if ($data['type'] === 'pgp') {
|
||||
$keyInfo = $Model->verifySingleGPG($data);
|
||||
$data['status'] = __('OK');
|
||||
$data['fingerprint'] = __('N/A');
|
||||
if (!$keyInfo[0]) {
|
||||
$data['status'] = $keyInfo[2];
|
||||
}
|
||||
if (!empty($keyInfo[4])) {
|
||||
$data['fingerprint'] = $keyInfo[4];
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
|
@ -155,8 +171,22 @@ class EncryptionKeysController extends AppController
|
|||
|
||||
public function view($id = false)
|
||||
{
|
||||
$this->EncryptionKeys->initializeGpg();
|
||||
$Model = $this->EncryptionKeys;
|
||||
$this->CRUD->view($id, [
|
||||
'contain' => ['Individuals', 'Organisations']
|
||||
'contain' => ['Individuals', 'Organisations'],
|
||||
'afterFind' => function($data) use ($Model) {
|
||||
if ($data['type'] === 'pgp') {
|
||||
$keyInfo = $Model->verifySingleGPG($data);
|
||||
if (!$keyInfo[0]) {
|
||||
$data['pgp_error'] = $keyInfo[2];
|
||||
}
|
||||
if (!empty($keyInfo[4])) {
|
||||
$data['pgp_fingerprint'] = $keyInfo[4];
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Tools;
|
||||
|
||||
use Cake\Core\Exception\Exception;
|
||||
use Cake\Core\Configure;
|
||||
|
||||
class CryptGpgExtended extends \Crypt_GPG
|
||||
{
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (!method_exists($this, '_prepareInput')) {
|
||||
$reflector = new \ReflectionClass('Crypt_GPG');
|
||||
$classPath = $reflector->getFileName();
|
||||
throw new Exception("Crypt_GPG class from '$classPath' is too old, at least version 1.6.1 is required.");
|
||||
}
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the smallest public key possible from the keyring.
|
||||
*
|
||||
* This removes all signatures except the most recent self-signature on each user ID. This option is the same as
|
||||
* running the --edit-key command "minimize" before export except that the local copy of the key is not modified.
|
||||
*
|
||||
* The exported key remains on the keyring. To delete the public key, use
|
||||
* {@link Crypt_GPG::deletePublicKey()}.
|
||||
*
|
||||
* If more than one key fingerprint is available for the specified
|
||||
* <kbd>$keyId</kbd> (for example, if you use a non-unique uid) only the
|
||||
* first public key is exported.
|
||||
*
|
||||
* @param string $keyId either the full uid of the public key, the email
|
||||
* part of the uid of the public key or the key id of
|
||||
* the public key. For example,
|
||||
* "Test User (example) <test@example.com>",
|
||||
* "test@example.com" or a hexadecimal string.
|
||||
* @param boolean $armor optional. If true, ASCII armored data is returned;
|
||||
* otherwise, binary data is returned. Defaults to
|
||||
* true.
|
||||
*
|
||||
* @return string the public key data.
|
||||
*
|
||||
* @throws Crypt_GPG_KeyNotFoundException if a public key with the given
|
||||
* <kbd>$keyId</kbd> is not found.
|
||||
*
|
||||
* @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
|
||||
* Use the <kbd>debug</kbd> option and file a bug report if these
|
||||
* exceptions occur.
|
||||
*/
|
||||
public function exportPublicKeyMinimal($keyId, $armor = true)
|
||||
{
|
||||
$fingerprint = $this->getFingerprint($keyId);
|
||||
|
||||
if ($fingerprint === null) {
|
||||
throw new \Crypt_GPG_KeyNotFoundException(
|
||||
'Key not found: ' . $keyId,
|
||||
self::ERROR_KEY_NOT_FOUND,
|
||||
$keyId
|
||||
);
|
||||
}
|
||||
|
||||
$keyData = '';
|
||||
$operation = '--export';
|
||||
$operation .= ' ' . escapeshellarg($fingerprint);
|
||||
|
||||
$arguments = array('--export-options', 'export-minimal');
|
||||
if ($armor) {
|
||||
$arguments[] = '--armor';
|
||||
}
|
||||
|
||||
$this->engine->reset();
|
||||
$this->engine->setPins($this->passphrases);
|
||||
$this->engine->setOutput($keyData);
|
||||
$this->engine->setOperation($operation, $arguments);
|
||||
$this->engine->run();
|
||||
|
||||
return $keyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return key info without importing it when GPG supports --import-options show-only, otherwise just import and
|
||||
* then return details.
|
||||
*
|
||||
* @param string $key
|
||||
* @return Crypt_GPG_Key[]
|
||||
* @throws Crypt_GPG_Exception
|
||||
* @throws Crypt_GPG_InvalidOperationException
|
||||
*/
|
||||
public function keyInfo($key)
|
||||
{
|
||||
$version = $this->engine->getVersion();
|
||||
if (version_compare($version, '2.1.23', 'le')) {
|
||||
$importResult = $this->importKey($key);
|
||||
$keys = [];
|
||||
foreach ($importResult['fingerprints'] as $fingerprint) {
|
||||
foreach ($this->getKeys($fingerprint) as $key) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
$input = $this->_prepareInput($key, false, false);
|
||||
|
||||
$output = '';
|
||||
$this->engine->reset();
|
||||
$this->engine->setInput($input);
|
||||
$this->engine->setOutput($output);
|
||||
$this->engine->setOperation('--import', ['--import-options', 'show-only', '--with-colons']);
|
||||
$this->engine->run();
|
||||
|
||||
$keys = [];
|
||||
$key = null; // current key
|
||||
$subKey = null; // current sub-key
|
||||
|
||||
foreach (explode(PHP_EOL, $output) as $line) {
|
||||
$lineExp = explode(':', $line);
|
||||
|
||||
if ($lineExp[0] === 'pub') {
|
||||
// new primary key means last key should be added to the array
|
||||
if ($key !== null) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
|
||||
$key = new \Crypt_GPG_Key();
|
||||
|
||||
$subKey = \Crypt_GPG_SubKey::parse($line);
|
||||
$key->addSubKey($subKey);
|
||||
|
||||
} elseif ($lineExp[0] === 'sub') {
|
||||
$subKey = \Crypt_GPG_SubKey::parse($line);
|
||||
$key->addSubKey($subKey);
|
||||
|
||||
} elseif ($lineExp[0] === 'fpr') {
|
||||
$fingerprint = $lineExp[9];
|
||||
|
||||
// set current sub-key fingerprint
|
||||
$subKey->setFingerprint($fingerprint);
|
||||
|
||||
} elseif ($lineExp[0] === 'uid') {
|
||||
$string = stripcslashes($lineExp[9]); // as per documentation
|
||||
$userId = new \Crypt_GPG_UserId($string);
|
||||
|
||||
if ($lineExp[1] === 'r') {
|
||||
$userId->setRevoked(true);
|
||||
}
|
||||
|
||||
$key->addUserId($userId);
|
||||
}
|
||||
}
|
||||
|
||||
// add last key
|
||||
if ($key !== null) {
|
||||
$keys[] = $key;
|
||||
} else {
|
||||
throw new \Crypt_GPG_Exception("Key data provided, but gpg process output could not be parsed: $output");
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return string
|
||||
* @throws Crypt_GPG_Exception
|
||||
* @throws Crypt_GPG_InvalidOperationException
|
||||
*/
|
||||
public function enarmor($key)
|
||||
{
|
||||
$input = $this->_prepareInput($key, false, false);
|
||||
|
||||
$armored = '';
|
||||
$this->engine->reset();
|
||||
$this->engine->setInput($input);
|
||||
$this->engine->setOutput($armored);
|
||||
$this->engine->setOperation('--enarmor');
|
||||
$this->engine->run();
|
||||
|
||||
return $armored;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @param bool $isFile
|
||||
* @param bool $allowEmpty
|
||||
* @return resource|string|null
|
||||
* @throws Crypt_GPG_FileException
|
||||
* @throws Crypt_GPG_NoDataException
|
||||
*/
|
||||
protected function _prepareInput($data, $isFile = false, $allowEmpty = true)
|
||||
{
|
||||
if ($isFile && $data instanceof TmpFileTool) {
|
||||
return $data->resource();
|
||||
}
|
||||
|
||||
return parent::_prepareInput($data, $isFile, $allowEmpty);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Tools;
|
||||
|
||||
use Cake\Core\Exception\Exception;
|
||||
use Cake\Core\Configure;
|
||||
|
||||
class GpgTool
|
||||
{
|
||||
/** @var CryptGpgExtended */
|
||||
private $gpg;
|
||||
|
||||
/**
|
||||
* @return CryptGpgExtended
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function initializeGpg()
|
||||
{
|
||||
if (!class_exists('Crypt_GPG')) {
|
||||
// 'Crypt_GPG' class cannot be autoloaded, try to require from include_path.
|
||||
if (!stream_resolve_include_path('Crypt/GPG.php')) {
|
||||
throw new Exception("Crypt_GPG is not installed.");
|
||||
}
|
||||
require_once 'Crypt/GPG.php';
|
||||
}
|
||||
require_once ROOT . '/src/Lib/Tools/CryptGpgExtended.php';
|
||||
|
||||
$homedir = Configure::read('GnuPG.homedir') ?? ROOT . '/.gnupg';
|
||||
if ($homedir === null) {
|
||||
throw new Exception("Configuration option 'GnuPG.homedir' is not set, Crypt_GPG cannot be initialized.");
|
||||
}
|
||||
|
||||
$options = [
|
||||
'homedir' => $homedir,
|
||||
'gpgconf' => Configure::read('GnuPG.gpgconf'),
|
||||
'binary' => Configure::read('GnuPG.binary') ?: '/usr/bin/gpg',
|
||||
];
|
||||
return new CryptGpgExtended($options);
|
||||
}
|
||||
|
||||
public function __construct(CryptGpgExtended $gpg = null)
|
||||
{
|
||||
$this->gpg = $gpg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function searchGpgKey($search)
|
||||
{
|
||||
$uri = 'https://openpgp.circl.lu/pks/lookup?search=' . urlencode($search) . '&op=index&fingerprint=on&options=mr';
|
||||
try {
|
||||
$response = $this->keyServerLookup($uri);
|
||||
} catch (HttpSocketHttpException $e) {
|
||||
if ($e->getCode() === 404) {
|
||||
return [];
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
return $this->extractKeySearch($response->body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fingerprint
|
||||
* @return string|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetchGpgKey($fingerprint)
|
||||
{
|
||||
$uri = 'https://openpgp.circl.lu/pks/lookup?search=0x' . urlencode($fingerprint) . '&op=get&options=mr';
|
||||
try {
|
||||
$response = $this->keyServerLookup($uri);
|
||||
} catch (HttpSocketHttpException $e) {
|
||||
if ($e->getCode() === 404) {
|
||||
return null;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$key = $response->body;
|
||||
|
||||
if ($this->gpg) {
|
||||
$fetchedFingerprint = $this->validateGpgKey($key);
|
||||
if (strtolower($fingerprint) !== strtolower($fetchedFingerprint)) {
|
||||
throw new Exception("Requested fingerprint do not match with fetched key fingerprint ($fingerprint != $fetchedFingerprint)");
|
||||
}
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates PGP key
|
||||
* @param string $keyData
|
||||
* @return string Primary key fingerprint
|
||||
* @throws Exception
|
||||
*/
|
||||
public function validateGpgKey($keyData)
|
||||
{
|
||||
if (!$this->gpg instanceof CryptGpgExtended) {
|
||||
throw new InvalidArgumentException("Valid CryptGpgExtended instance required.");
|
||||
}
|
||||
$fetchedKeyInfo = $this->gpg->keyInfo($keyData);
|
||||
if (count($fetchedKeyInfo) !== 1) {
|
||||
throw new Exception("Multiple keys found");
|
||||
}
|
||||
$primaryKey = $fetchedKeyInfo[0]->getPrimaryKey();
|
||||
if (empty($primaryKey)) {
|
||||
throw new Exception("No primary key found");
|
||||
}
|
||||
$this->gpg->importKey($keyData);
|
||||
return $primaryKey->getFingerprint();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return array
|
||||
*/
|
||||
private function extractKeySearch($body)
|
||||
{
|
||||
$final = array();
|
||||
$lines = explode("\n", $body);
|
||||
foreach ($lines as $line) {
|
||||
$parts = explode(":", $line);
|
||||
|
||||
if ($parts[0] === 'pub') {
|
||||
if (!empty($temp)) {
|
||||
$final[] = $temp;
|
||||
$temp = array();
|
||||
}
|
||||
|
||||
if (strpos($parts[6], 'r') !== false || strpos($parts[6], 'd') !== false || strpos($parts[6], 'e') !== false) {
|
||||
continue; // skip if key is expired, revoked or disabled
|
||||
}
|
||||
|
||||
$temp = array(
|
||||
'fingerprint' => $parts[1],
|
||||
'key_id' => substr($parts[1], -8),
|
||||
'date' => date('Y-m-d', $parts[4]),
|
||||
);
|
||||
|
||||
} else if ($parts[0] === 'uid' && !empty($temp)) {
|
||||
$temp['address'] = urldecode($parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($temp)) {
|
||||
$final[] = $temp;
|
||||
}
|
||||
|
||||
return $final;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/draft-koch-openpgp-webkey-service-10
|
||||
* @param string $email
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function wkd($email)
|
||||
{
|
||||
if (!$this->gpg instanceof CryptGpgExtended) {
|
||||
throw new InvalidArgumentException("Valid CryptGpgExtended instance required.");
|
||||
}
|
||||
|
||||
$parts = explode('@', $email);
|
||||
if (count($parts) !== 2) {
|
||||
throw new InvalidArgumentException("Invalid e-mail address provided.");
|
||||
}
|
||||
|
||||
list($localPart, $domain) = $parts;
|
||||
$localPart = strtolower($localPart);
|
||||
$localPartHash = $this->zbase32(sha1($localPart, true));
|
||||
|
||||
$advancedUrl = "https://openpgpkey.$domain/.well-known/openpgpkey/" . strtolower($domain) . "/hu/$localPartHash";
|
||||
try {
|
||||
$response = $this->keyServerLookup($advancedUrl);
|
||||
return $this->gpg->enarmor($response->body());
|
||||
} catch (Exception $e) {
|
||||
// pass, continue to direct method
|
||||
}
|
||||
|
||||
$directUrl = "https://$domain/.well-known/openpgpkey/hu/$localPartHash";
|
||||
try {
|
||||
$response = $this->keyServerLookup($directUrl);
|
||||
} catch (HttpSocketHttpException $e) {
|
||||
if ($e->getCode() === 404) {
|
||||
throw new NotFoundException("Key not found");
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
return $this->gpg->enarmor($response->body());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts data to zbase32 string.
|
||||
*
|
||||
* @see http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
private function zbase32($data)
|
||||
{
|
||||
$chars = 'ybndrfg8ejkmcpqxot1uwisza345h769'; // lower-case
|
||||
$res = '';
|
||||
$remainder = 0;
|
||||
$remainderSize = 0;
|
||||
|
||||
for ($i = 0; $i < strlen($data); $i++) {
|
||||
$b = ord($data[$i]);
|
||||
$remainder = ($remainder << 8) | $b;
|
||||
$remainderSize += 8;
|
||||
while ($remainderSize > 4) {
|
||||
$remainderSize -= 5;
|
||||
$c = $remainder & (31 << $remainderSize);
|
||||
$c >>= $remainderSize;
|
||||
$res .= $chars[$c];
|
||||
}
|
||||
}
|
||||
if ($remainderSize > 0) {
|
||||
// remainderSize < 5:
|
||||
$remainder <<= (5 - $remainderSize);
|
||||
$c = $remainder & 31;
|
||||
$res .= $chars[$c];
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @return HttpSocketResponseExtended
|
||||
* @throws HttpSocketHttpException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function keyServerLookup($uri)
|
||||
{
|
||||
App::uses('SyncTool', 'Tools');
|
||||
$syncTool = new SyncTool();
|
||||
$HttpSocket = $syncTool->createHttpSocket(['compress' => true]);
|
||||
$response = $HttpSocket->get($uri);
|
||||
if (!$response->isOk()) {
|
||||
throw new HttpSocketHttpException($response, $uri);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,9 @@ use ArrayObject;
|
|||
|
||||
class EncryptionKeysTable extends AppTable
|
||||
{
|
||||
|
||||
public $gpg = null;
|
||||
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
|
@ -56,4 +59,92 @@ class EncryptionKeysTable extends AppTable
|
|||
->requirePresence(['type', 'encryption_key', 'owner_id', 'owner_model'], 'create');
|
||||
return $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 0 - true if key is valid
|
||||
* 1 - User e-mail
|
||||
* 2 - Error message
|
||||
* 3 - Not used
|
||||
* 4 - Key fingerprint
|
||||
* 5 - Key fingerprint
|
||||
* @param \App\Model\Entity\EncryptionKey $encryptionKey
|
||||
* @return array
|
||||
*/
|
||||
public function verifySingleGPG(\App\Model\Entity\EncryptionKey $encryptionKey): array
|
||||
{
|
||||
$result = [0 => false, 1 => null];
|
||||
|
||||
$gpg = $this->initializeGpg();
|
||||
if (!$gpg) {
|
||||
$result[2] = 'GnuPG is not configured on this system.';
|
||||
return $result;
|
||||
}
|
||||
|
||||
try {
|
||||
$currentTimestamp = time();
|
||||
$keys = $gpg->keyInfo($encryptionKey['encryption_key']);
|
||||
if (count($keys) !== 1) {
|
||||
$result[2] = 'Multiple or no key found';
|
||||
return $result;
|
||||
}
|
||||
|
||||
$key = $keys[0];
|
||||
$result[4] = $key->getPrimaryKey()->getFingerprint();
|
||||
$result[5] = $result[4];
|
||||
|
||||
$sortedKeys = ['valid' => 0, 'expired' => 0, 'noEncrypt' => 0];
|
||||
foreach ($key->getSubKeys() as $subKey) {
|
||||
$expiration = $subKey->getExpirationDate();
|
||||
if ($expiration != 0 && $currentTimestamp > $expiration) {
|
||||
$sortedKeys['expired']++;
|
||||
continue;
|
||||
}
|
||||
if (!$subKey->canEncrypt()) {
|
||||
$sortedKeys['noEncrypt']++;
|
||||
continue;
|
||||
}
|
||||
$sortedKeys['valid']++;
|
||||
}
|
||||
if (!$sortedKeys['valid']) {
|
||||
$result[2] = 'The user\'s PGP key does not include a valid subkey that could be used for encryption.';
|
||||
if ($sortedKeys['expired']) {
|
||||
$result[2] .= ' ' . __n('Found %s subkey that have expired.', 'Found %s subkeys that have expired.', $sortedKeys['expired'], $sortedKeys['expired']);
|
||||
}
|
||||
if ($sortedKeys['noEncrypt']) {
|
||||
$result[2] .= ' ' . __n('Found %s subkey that is sign only.', 'Found %s subkeys that are sign only.', $sortedKeys['noEncrypt'], $sortedKeys['noEncrypt']);
|
||||
}
|
||||
} else {
|
||||
$result[0] = true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$result[2] = $e->getMessage();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize GPG. Returns `null` if initialization failed.
|
||||
*
|
||||
* @return null|CryptGpgExtended
|
||||
*/
|
||||
public function initializeGpg()
|
||||
{
|
||||
require_once(ROOT . '/src/Lib/Tools/GpgTool.php');
|
||||
if ($this->gpg !== null) {
|
||||
if ($this->gpg === false) { // initialization failed
|
||||
return null;
|
||||
}
|
||||
return $this->gpg;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->gpg = \App\Lib\Tools\GpgTool::initializeGpg();
|
||||
return $this->gpg;
|
||||
} catch (\Exception $e) {
|
||||
//$this->logException("GPG couldn't be initialized, GPG encryption and signing will be not available.", $e, LOG_NOTICE);
|
||||
$this->gpg = false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'owner_model_path' => 'owner_model',
|
||||
'element' => 'owner'
|
||||
],
|
||||
[
|
||||
'name' => __('Revoked'),
|
||||
'data_path' => 'fingerprint'
|
||||
],
|
||||
[
|
||||
'name' => __('Status'),
|
||||
'data_path' => 'status'
|
||||
],
|
||||
[
|
||||
'name' => __('Revoked'),
|
||||
'sort' => 'revoked',
|
||||
|
@ -56,7 +64,8 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
],
|
||||
[
|
||||
'name' => __('Key'),
|
||||
'data_path' => 'encryption_key'
|
||||
'data_path' => 'encryption_key',
|
||||
'element' => 'pgp_key'
|
||||
],
|
||||
],
|
||||
'title' => __('Encryption key Index'),
|
||||
|
|
|
@ -1,32 +1,53 @@
|
|||
<?php
|
||||
echo $this->element(
|
||||
'/genericElements/SingleViews/single_view',
|
||||
[
|
||||
'data' => $entity,
|
||||
'fields' => [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => 'id'
|
||||
],
|
||||
[
|
||||
'key' => __('Type'),
|
||||
'path' => 'type'
|
||||
],
|
||||
[
|
||||
'key' => __('Owner'),
|
||||
'path' => 'owner_id',
|
||||
'owner_model_path' => 'owner_model',
|
||||
'type' => 'owner'
|
||||
],
|
||||
[
|
||||
'key' => __('Revoked'),
|
||||
'path' => 'revoked'
|
||||
],
|
||||
|
||||
[
|
||||
'key' => __('Key'),
|
||||
'path' => 'encryption_key'
|
||||
]
|
||||
$fields = [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => 'id'
|
||||
],
|
||||
[
|
||||
'key' => __('Type'),
|
||||
'path' => 'type'
|
||||
],
|
||||
[
|
||||
'key' => __('Owner'),
|
||||
'path' => 'owner_id',
|
||||
'owner_model_path' => 'owner_model',
|
||||
'type' => 'owner'
|
||||
],
|
||||
[
|
||||
'key' => __('Revoked'),
|
||||
'path' => 'revoked',
|
||||
'type' => 'boolean'
|
||||
],
|
||||
[
|
||||
'key' => __('Key'),
|
||||
'path' => 'encryption_key',
|
||||
'type' => 'key'
|
||||
]
|
||||
]
|
||||
);
|
||||
];
|
||||
if ($entity['type'] === 'pgp') {
|
||||
if (!empty($entity['pgp_fingerprint'])) {
|
||||
$fields[] = [
|
||||
'key' => __('Fingerprint'),
|
||||
'path' => 'pgp_fingerprint'
|
||||
];
|
||||
}
|
||||
if (!empty($entity['pgp_error'])) {
|
||||
$fields[] = [
|
||||
'key' => __('PGP Status'),
|
||||
'path' => 'pgp_error'
|
||||
];
|
||||
} else {
|
||||
$fields[] = [
|
||||
'key' => __('PGP Status'),
|
||||
'raw' => __('OK')
|
||||
];
|
||||
}
|
||||
}
|
||||
echo $this->element(
|
||||
'/genericElements/SingleViews/single_view',
|
||||
[
|
||||
'data' => $entity,
|
||||
'fields' => $fields
|
||||
]
|
||||
);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
$value = Cake\Utility\Hash::extract($row, $field['data_path']);
|
||||
$value = empty($value[0]) ? '' : $value[0];
|
||||
echo $this->element('/genericElements/key', ['value' => $value, 'description' => $description ?? null]);
|
||||
?>
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
$value = Cake\Utility\Hash::extract($data, $field['path']);
|
||||
$value = empty($value[0]) ? '' : $value[0];
|
||||
echo $this->element('/genericElements/key', ['value' => $value, 'description' => $description ?? null]);
|
||||
?>
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
if (empty($value)) {
|
||||
echo sprintf(
|
||||
'<span class="bold red">%s</span>',
|
||||
__('N/A')
|
||||
);
|
||||
} else {
|
||||
echo sprintf(
|
||||
'<details>%s%s</details>',
|
||||
!empty($description) ?
|
||||
sprintf(
|
||||
'<summary style="cursor: pointer">%s</summary>',
|
||||
h($description)
|
||||
) : '',
|
||||
sprintf(
|
||||
'<pre class="quickSelect" style="line-height: 1.44">%s</pre>',
|
||||
h($value)
|
||||
)
|
||||
);
|
||||
}
|
||||
?>
|
Loading…
Reference in New Issue