first commit
This commit is contained in:
7
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/autoload.php
vendored
Normal file
7
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/autoload.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInita8412ede23fd11b4d0e29303fdebd5f4::getLoader();
|
||||
479
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/ClassLoader.php
vendored
Normal file
479
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/ClassLoader.php
vendored
Normal file
@@ -0,0 +1,479 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
}
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||
*
|
||||
* @return self[]
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
303
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/InstalledVersions.php
vendored
Normal file
303
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/InstalledVersions.php
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class InstalledVersions
|
||||
{
|
||||
private static $installed = array (
|
||||
'root' =>
|
||||
array (
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '53cfe01c831d81b1398d479a9e85cbb4110e9e13',
|
||||
'name' => '__root__',
|
||||
),
|
||||
'versions' =>
|
||||
array (
|
||||
'__root__' =>
|
||||
array (
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '53cfe01c831d81b1398d479a9e85cbb4110e9e13',
|
||||
),
|
||||
'fbett/le_acme2' =>
|
||||
array (
|
||||
'pretty_version' => '1.5.6',
|
||||
'version' => '1.5.6.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '26b2c421764b173326f6bcb0713a86bd614f77fa',
|
||||
),
|
||||
'plesk/api-php-lib' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.0.7',
|
||||
'version' => '1.0.7.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '7f81b0c3bb0a9f4200aef62a54d3e2c04d91a605',
|
||||
),
|
||||
),
|
||||
);
|
||||
private static $canGetVendors;
|
||||
private static $installedByVendor = array();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function isInstalled($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints($constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function getRawData()
|
||||
{
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$installed[] = self::$installed;
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
21
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/LICENSE
vendored
Normal file
21
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
10
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_classmap.php
vendored
Normal file
10
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_classmap.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
);
|
||||
10
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_namespaces.php
vendored
Normal file
10
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_namespaces.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'LE_ACME2' => array($vendorDir . '/fbett/le_acme2/src'),
|
||||
);
|
||||
10
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_psr4.php
vendored
Normal file
10
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_psr4.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'PleskX\\' => array($vendorDir . '/plesk/api-php-lib/src'),
|
||||
);
|
||||
57
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_real.php
vendored
Normal file
57
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_real.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInita8412ede23fd11b4d0e29303fdebd5f4
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInita8412ede23fd11b4d0e29303fdebd5f4', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInita8412ede23fd11b4d0e29303fdebd5f4', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInita8412ede23fd11b4d0e29303fdebd5f4::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
47
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_static.php
vendored
Normal file
47
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/autoload_static.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInita8412ede23fd11b4d0e29303fdebd5f4
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'P' =>
|
||||
array (
|
||||
'PleskX\\' => 7,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'PleskX\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/plesk/api-php-lib/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'L' =>
|
||||
array (
|
||||
'LE_ACME2' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/fbett/le_acme2/src',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInita8412ede23fd11b4d0e29303fdebd5f4::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInita8412ede23fd11b4d0e29303fdebd5f4::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInita8412ede23fd11b4d0e29303fdebd5f4::$prefixesPsr0;
|
||||
$loader->classMap = ComposerStaticInita8412ede23fd11b4d0e29303fdebd5f4::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
1
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/index.php
vendored
Normal file
1
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/index.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<?php // You don't belong here. ?>
|
||||
113
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/installed.json
vendored
Normal file
113
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/installed.json
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "fbett/le_acme2",
|
||||
"version": "1.5.6",
|
||||
"version_normalized": "1.5.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fbett/le-acme2-php.git",
|
||||
"reference": "26b2c421764b173326f6bcb0713a86bd614f77fa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fbett/le-acme2-php/zipball/26b2c421764b173326f6bcb0713a86bd614f77fa",
|
||||
"reference": "26b2c421764b173326f6bcb0713a86bd614f77fa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"php": ">=7.3"
|
||||
},
|
||||
"time": "2021-05-17T07:08:46+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"LE_ACME2": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabian Bett",
|
||||
"homepage": "https://www.bett-ingenieure.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Letsencrypt PHP ACME v2 client",
|
||||
"homepage": "https://github.com/fbett/le-acme2-php",
|
||||
"support": {
|
||||
"issues": "https://github.com/fbett/le-acme2-php/issues",
|
||||
"source": "https://github.com/fbett/le-acme2-php/tree/v1.5.6"
|
||||
},
|
||||
"install-path": "../fbett/le_acme2"
|
||||
},
|
||||
{
|
||||
"name": "plesk/api-php-lib",
|
||||
"version": "v1.0.7",
|
||||
"version_normalized": "1.0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/plesk/api-php-lib.git",
|
||||
"reference": "7f81b0c3bb0a9f4200aef62a54d3e2c04d91a605"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/plesk/api-php-lib/zipball/7f81b0c3bb0a9f4200aef62a54d3e2c04d91a605",
|
||||
"reference": "7f81b0c3bb0a9f4200aef62a54d3e2c04d91a605",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"php": "^7.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"spatie/phpunit-watcher": "^1.22"
|
||||
},
|
||||
"time": "2020-12-24T07:20:26+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PleskX\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alexei Yuzhakov",
|
||||
"email": "sibprogrammer@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Plesk International GmbH.",
|
||||
"email": "plesk-dev-leads@plesk.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP object-oriented library for Plesk XML-RPC API",
|
||||
"support": {
|
||||
"issues": "https://github.com/plesk/api-php-lib/issues",
|
||||
"source": "https://github.com/plesk/api-php-lib/tree/v1.0.7"
|
||||
},
|
||||
"install-path": "../plesk/api-php-lib"
|
||||
}
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": []
|
||||
}
|
||||
42
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/installed.php
vendored
Normal file
42
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/installed.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php return array (
|
||||
'root' =>
|
||||
array (
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '53cfe01c831d81b1398d479a9e85cbb4110e9e13',
|
||||
'name' => '__root__',
|
||||
),
|
||||
'versions' =>
|
||||
array (
|
||||
'__root__' =>
|
||||
array (
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '53cfe01c831d81b1398d479a9e85cbb4110e9e13',
|
||||
),
|
||||
'fbett/le_acme2' =>
|
||||
array (
|
||||
'pretty_version' => '1.5.6',
|
||||
'version' => '1.5.6.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '26b2c421764b173326f6bcb0713a86bd614f77fa',
|
||||
),
|
||||
'plesk/api-php-lib' =>
|
||||
array (
|
||||
'pretty_version' => 'v1.0.7',
|
||||
'version' => '1.0.7.0',
|
||||
'aliases' =>
|
||||
array (
|
||||
),
|
||||
'reference' => '7f81b0c3bb0a9f4200aef62a54d3e2c04d91a605',
|
||||
),
|
||||
),
|
||||
);
|
||||
26
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/platform_check.php
vendored
Normal file
26
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/composer/platform_check.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70100)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
1
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/index.php
vendored
Normal file
1
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/index.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<?php // You don't belong here. ?>
|
||||
19
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/LICENSE.md
vendored
Normal file
19
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/LICENSE.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
119
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/README.md
vendored
Normal file
119
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/README.md
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
# le-acme2-php
|
||||
LetsEncrypt client library for ACME v2 written in PHP.
|
||||
|
||||
This library is inspired by [yourivw/LEClient](https://github.com/yourivw/LEClient), completely rewritten and enhanced with some new features:
|
||||
- Support for Composer autoload (including separated Namespaces)
|
||||
- Automatic renewal process
|
||||
- Managed HTTP authentication process
|
||||
- Response caching mechanism
|
||||
- Prevents blocking while waiting for server results
|
||||
- Optional certificate feature "OCSP Must-Staple"
|
||||
- Optional set a preferred chain
|
||||
|
||||
The aim of this client is to make an easy-to-use and integrated solution to create a LetsEncrypt-issued SSL/TLS certificate with PHP.
|
||||
|
||||
You have the possibility to use the HTTP authentication:
|
||||
You need to be able to redirect specific requests (see below)
|
||||
|
||||
You have also the possibility to use DNS authentication:
|
||||
You need to be able to set dynamic DNS configurations.
|
||||
|
||||
Wildcard certificates can only be requested by using the dns authentication.
|
||||
|
||||
## Current version
|
||||
|
||||
Tested with LetsEncrypt staging and production servers.
|
||||
|
||||
[Transitioning to ISRG's Root](https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html):
|
||||
|
||||
This library supports it to set a preferred chain in `Order::setPreferredChain($issuerCN))`.
|
||||
|
||||
If the preferred chain is not set or set to IdenTrust’s chain,
|
||||
this library will try to use the IdenTrust’s chain as long as possible.
|
||||
Please see: https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The minimum required PHP version is 7.3.
|
||||
|
||||
This client also depends on cURL and OpenSSL.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Install via composer:
|
||||
|
||||
```
|
||||
composer require fbett/le_acme2
|
||||
```
|
||||
|
||||
Also have a look at the [LetsEncrypt documentation](https://letsencrypt.org/docs/) for more information and documentation on LetsEncrypt and ACME.
|
||||
|
||||
## Example Integration
|
||||
|
||||
- Create a working directory.
|
||||
Warning: This directory will also include private keys, so i suggest to place this directory somewhere not in the root document path of the web server.
|
||||
Additionally this directory should be protected to be read from other web server users.
|
||||
|
||||
```
|
||||
mkdir /etc/ssl/le-storage/
|
||||
chown root:root /etc/ssl/le-storage
|
||||
chmod 0600 /etc/ssl/le-storage
|
||||
```
|
||||
|
||||
- (HTTP authorization only) Create a directory for the acme challenges. It must be reachable by http/https.
|
||||
|
||||
```
|
||||
mkdir /var/www/acme-challenges
|
||||
```
|
||||
|
||||
- (HTTP authorization only) Redirect specific requests to your acme-challenges directory
|
||||
|
||||
Example apache virtual host configuration:
|
||||
|
||||
```
|
||||
<VirtualHost ...>
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule \.well-known/acme-challenge/(.*)$ https://your-domain.com/path/to/acme-challenges/$1 [R=302,L]
|
||||
</IfModule>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
- (DNS authorization only) Set the DNS configuration
|
||||
|
||||
If `DNSWriter::write(...)` is called, set the DNS configuration like described in:
|
||||
|
||||
[https://letsencrypt.org/docs/challenge-types/#dns-01-challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge)
|
||||
|
||||
(By adding the digest as a TXT record for the subdomain '_acme-challenge'.)
|
||||
|
||||
|
||||
- Use the certificate bundle, if the certificate is issued:
|
||||
|
||||
```
|
||||
if($order->isCertificateBundleAvailable()) {
|
||||
|
||||
$bundle = $order->getCertificateBundle();
|
||||
|
||||
$pathToPrivateKey = $bundle->path . $bundle->private;
|
||||
$pathToCertificate = $bundle->path . $bundle->certificate;
|
||||
$pathToIntermediate = $bundle->path . $bundle->intermediate;
|
||||
|
||||
$order->enableAutoRenewal(); // If the date of expiration is closer than thirty days, the order will automatically start the renewal process.
|
||||
}
|
||||
```
|
||||
|
||||
If a certificate is renewed, the path will also change.
|
||||
|
||||
My integrated workflow is the following:
|
||||
- User enables SSL to a specific domain in my control panel
|
||||
- The cronjob of this control panel will detect these changes and tries to create or get an order like in the sample.
|
||||
- The cronjob will fetch the information within the certificate bundle, if the certificate bundle is ready (mostly on the second run for challenge type HTTP and on the third run for challenge type DNS)
|
||||
- The cronjob will also build the Apache virtual host files and will restart the Apache2 service, if the new config file is different.
|
||||
|
||||
Please take a look on the Samples for a full sample workflow.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
|
||||
25
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/composer.json
vendored
Normal file
25
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/composer.json
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "fbett/le_acme2",
|
||||
"description": "Letsencrypt PHP ACME v2 client",
|
||||
"homepage": "https://github.com/fbett/le-acme2-php",
|
||||
"version": "1.5.6",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabian Bett",
|
||||
"homepage": "https://www.bett-ingenieure.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"LE_ACME2": "src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.3",
|
||||
"ext-curl": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-json": "*"
|
||||
}
|
||||
}
|
||||
25
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/phpunit.xml.dist
vendored
Normal file
25
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
forceCoversAnnotation="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="true"
|
||||
bootstrap="../../autoload.php"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||
>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./src/LE_ACME2</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="LE_ACME2 Test Suite">
|
||||
<directory>./src/LE_ACME2Tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Connector\Connector;
|
||||
|
||||
abstract class AbstractKeyValuable {
|
||||
|
||||
const KEY_TYPE_RSA = "RSA";
|
||||
const KEY_TYPE_EC = "EC";
|
||||
|
||||
protected $_identifier;
|
||||
|
||||
protected static $_directoryPath = null;
|
||||
|
||||
public static function setCommonKeyDirectoryPath(string $directoryPath) {
|
||||
|
||||
if(!file_exists($directoryPath)) {
|
||||
throw new \RuntimeException('Common Key Directory Path does not exist');
|
||||
}
|
||||
|
||||
self::$_directoryPath = realpath($directoryPath) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
public static function getCommonKeyDirectoryPath() : ?string {
|
||||
return self::$_directoryPath;
|
||||
}
|
||||
|
||||
protected function _getKeyDirectoryPath(string $appendix = '') : string {
|
||||
|
||||
return self::$_directoryPath . $this->_identifier . $appendix . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
public function getKeyDirectoryPath() : string {
|
||||
|
||||
return $this->_getKeyDirectoryPath('');
|
||||
}
|
||||
|
||||
protected function _initKeyDirectory(string $keyType = self::KEY_TYPE_RSA, bool $ignoreIfKeysExist = false) {
|
||||
|
||||
if(!file_exists($this->getKeyDirectoryPath())) {
|
||||
|
||||
mkdir($this->getKeyDirectoryPath());
|
||||
}
|
||||
|
||||
if(!$ignoreIfKeysExist && (
|
||||
file_exists($this->getKeyDirectoryPath() . 'private.pem') ||
|
||||
file_exists($this->getKeyDirectoryPath() . 'public.pem')
|
||||
)
|
||||
) {
|
||||
|
||||
throw new \RuntimeException(
|
||||
'Keys exist already. Exists the ' . get_class($this) . ' already?' . PHP_EOL .
|
||||
'Path: ' . $this->getKeyDirectoryPath()
|
||||
);
|
||||
}
|
||||
|
||||
if($keyType == self::KEY_TYPE_RSA) {
|
||||
|
||||
Utilities\KeyGenerator::RSA(
|
||||
$this->getKeyDirectoryPath(),
|
||||
'private.pem',
|
||||
'public.pem'
|
||||
);
|
||||
} else if($keyType == self::KEY_TYPE_EC) {
|
||||
|
||||
Utilities\KeyGenerator::EC(
|
||||
$this->getKeyDirectoryPath(),
|
||||
'private.pem',
|
||||
'public.pem'
|
||||
);
|
||||
} else {
|
||||
|
||||
throw new \RuntimeException('Key type "' . $keyType . '" not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function _clearKeyDirectory() {
|
||||
|
||||
if(file_exists($this->getKeyDirectoryPath() . 'private.pem')) {
|
||||
unlink($this->getKeyDirectoryPath() . 'private.pem');
|
||||
}
|
||||
|
||||
if(file_exists($this->getKeyDirectoryPath() . 'public.pem')) {
|
||||
unlink($this->getKeyDirectoryPath() . 'public.pem');
|
||||
}
|
||||
}
|
||||
|
||||
protected function _getAccountIdentifier(Account $account) : string {
|
||||
|
||||
$staging = Connector::getInstance()->isUsingStagingServer();
|
||||
|
||||
return 'account_' . ($staging ? 'staging_' : 'live_') . $account->getEmail();
|
||||
}
|
||||
}
|
||||
163
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/src/LE_ACME2/Account.php
vendored
Normal file
163
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/src/LE_ACME2/Account.php
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
namespace LE_ACME2;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class Account extends AbstractKeyValuable {
|
||||
|
||||
private $_email = NULL;
|
||||
|
||||
public function __construct(string $email) {
|
||||
|
||||
$this->_setEmail($email);
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_INFO,
|
||||
get_class() . '::' . __FUNCTION__ . ' email: "' . $email . '"'
|
||||
);
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' path: ' . $this->getKeyDirectoryPath()
|
||||
);
|
||||
}
|
||||
|
||||
private function _setEmail(string $email) {
|
||||
|
||||
$this->_email = $email;
|
||||
$this->_identifier = $this->_getAccountIdentifier($this);
|
||||
}
|
||||
|
||||
public function getEmail() : string {
|
||||
|
||||
return $this->_email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
* @return Account|null
|
||||
* @throws Exception\AbstractException
|
||||
*/
|
||||
public static function create(string $email) : Account {
|
||||
|
||||
$account = new self($email);
|
||||
$account->_initKeyDirectory();
|
||||
|
||||
$request = new Request\Account\Create($account);
|
||||
|
||||
try {
|
||||
$response = $request->getResponse();
|
||||
|
||||
Cache\AccountResponse::getInstance()->set($account, $response);
|
||||
|
||||
return $account;
|
||||
|
||||
} catch(Exception\AbstractException $e) {
|
||||
|
||||
$account->_clearKeyDirectory();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public static function exists(string $email) : bool {
|
||||
|
||||
$account = new self($email);
|
||||
|
||||
return file_exists($account->getKeyDirectoryPath()) &&
|
||||
file_exists($account->getKeyDirectoryPath() . 'private.pem') &&
|
||||
file_exists($account->getKeyDirectoryPath() . 'public.pem');
|
||||
}
|
||||
|
||||
public static function get(string $email) : Account {
|
||||
|
||||
$account = new self($email);
|
||||
|
||||
if(!self::exists($email))
|
||||
throw new \RuntimeException('Keys not found - does this account exist?');
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Account\GetData
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getData() : Response\Account\GetData {
|
||||
|
||||
$request = new Request\Account\GetData($this);
|
||||
return $request->getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
* @return bool
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function update(string $email) : bool {
|
||||
|
||||
$request = new Request\Account\Update($this, $email);
|
||||
|
||||
try {
|
||||
/* $response = */ $request->getResponse();
|
||||
|
||||
$previousKeyDirectoryPath = $this->getKeyDirectoryPath();
|
||||
|
||||
$this->_setEmail($email);
|
||||
|
||||
if($previousKeyDirectoryPath != $this->getKeyDirectoryPath())
|
||||
rename($previousKeyDirectoryPath, $this->getKeyDirectoryPath());
|
||||
|
||||
return true;
|
||||
|
||||
} catch(Exception\InvalidResponse $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function changeKeys() : bool {
|
||||
|
||||
Utilities\KeyGenerator::RSA($this->getKeyDirectoryPath(), 'private-replacement.pem', 'public-replacement.pem');
|
||||
|
||||
$request = new Request\Account\ChangeKeys($this);
|
||||
try {
|
||||
/* $response = */ $request->getResponse();
|
||||
|
||||
unlink($this->getKeyDirectoryPath() . 'private.pem');
|
||||
unlink($this->getKeyDirectoryPath() . 'public.pem');
|
||||
rename($this->getKeyDirectoryPath() . 'private-replacement.pem', $this->getKeyDirectoryPath() . 'private.pem');
|
||||
rename($this->getKeyDirectoryPath() . 'public-replacement.pem', $this->getKeyDirectoryPath() . 'public.pem');
|
||||
return true;
|
||||
|
||||
} catch(Exception\InvalidResponse $e) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function deactivate() : bool {
|
||||
|
||||
$request = new Request\Account\Deactivate($this);
|
||||
|
||||
try {
|
||||
/* $response = */ $request->getResponse();
|
||||
|
||||
return true;
|
||||
|
||||
} catch(Exception\InvalidResponse $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Authorizer;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
use LE_ACME2\Order;
|
||||
|
||||
abstract class AbstractAuthorizer {
|
||||
|
||||
protected $_account;
|
||||
protected $_order;
|
||||
|
||||
/**
|
||||
* AbstractAuthorizer constructor.
|
||||
*
|
||||
* @param Account $account
|
||||
* @param Order $order
|
||||
*
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
public function __construct(Account $account, Order $order) {
|
||||
|
||||
$this->_account = $account;
|
||||
$this->_order = $order;
|
||||
|
||||
$this->_fetchAuthorizationResponses();
|
||||
}
|
||||
|
||||
/** @var Response\Authorization\Get[] $_authorizationResponses */
|
||||
protected $_authorizationResponses = [];
|
||||
|
||||
/**
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
protected function _fetchAuthorizationResponses() {
|
||||
|
||||
if(!file_exists($this->_order->getKeyDirectoryPath() . 'private.pem')) {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' result suppressed (Order has finished already)'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$orderResponse = Cache\OrderResponse::getInstance()->get($this->_order);
|
||||
|
||||
foreach($orderResponse->getAuthorizations() as $authorization) {
|
||||
|
||||
$request = new Request\Authorization\Get($this->_account, $authorization);
|
||||
$this->_authorizationResponses[] = $request->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
protected function _hasValidAuthorizationResponses() : bool {
|
||||
|
||||
return count($this->_authorizationResponses) > 0;
|
||||
}
|
||||
|
||||
public function shouldStartAuthorization() : bool {
|
||||
|
||||
foreach($this->_authorizationResponses as $response) {
|
||||
|
||||
$challenge = $response->getChallenge($this->_getChallengeType());
|
||||
if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_PENDING) {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' "Pending challenge found',
|
||||
$challenge
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract protected function _getChallengeType() : string;
|
||||
|
||||
/**
|
||||
* @throws Exception\AuthorizationInvalid
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
public function progress() {
|
||||
|
||||
if(!$this->_hasValidAuthorizationResponses())
|
||||
return;
|
||||
|
||||
$existsNotValidChallenges = false;
|
||||
|
||||
foreach($this->_authorizationResponses as $authorizationResponse) {
|
||||
|
||||
$challenge = $authorizationResponse->getChallenge($this->_getChallengeType());
|
||||
|
||||
if($this->_existsNotValidChallenges($challenge, $authorizationResponse)) {
|
||||
$existsNotValidChallenges = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_finished = !$existsNotValidChallenges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response\Authorization\Struct\Challenge $challenge
|
||||
* @param Response\Authorization\Get $authorizationResponse
|
||||
* @return bool
|
||||
*
|
||||
* @throws Exception\AuthorizationInvalid
|
||||
*/
|
||||
protected function _existsNotValidChallenges(Response\Authorization\Struct\Challenge $challenge,
|
||||
Response\Authorization\Get $authorizationResponse
|
||||
) : bool {
|
||||
|
||||
if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_PENDING) {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' "Non valid challenge found',
|
||||
$challenge
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_PROGRESSING) {
|
||||
|
||||
// Should come back later
|
||||
return true;
|
||||
}
|
||||
else if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_VALID) {
|
||||
|
||||
}
|
||||
else if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_INVALID) {
|
||||
throw new Exception\AuthorizationInvalid(
|
||||
'Received status "' . Response\Authorization\Struct\Challenge::STATUS_INVALID . '" while challenge should be verified'
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
throw new \RuntimeException('Challenge status "' . $challenge->status . '" is not implemented');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected $_finished = false;
|
||||
|
||||
public function hasFinished() : bool {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_called_class() . '::' . __FUNCTION__,
|
||||
$this->_finished
|
||||
);
|
||||
|
||||
return $this->_finished;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Authorizer;
|
||||
defined('ABSPATH') or die();
|
||||
use LE_ACME2\Order;
|
||||
|
||||
abstract class AbstractDNSWriter {
|
||||
|
||||
/**
|
||||
* @param Order $order
|
||||
* @param string $identifier
|
||||
* @param string $digest
|
||||
*
|
||||
* @return bool return true, if the dns configuration is usable and the process should be progressed
|
||||
*/
|
||||
abstract public function write(Order $order, string $identifier, string $digest) : bool;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Authorizer;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Order;
|
||||
use LE_ACME2\Struct\ChallengeAuthorizationKey;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
class DNS extends AbstractAuthorizer {
|
||||
|
||||
protected function _getChallengeType(): string {
|
||||
return Order::CHALLENGE_TYPE_DNS;
|
||||
}
|
||||
|
||||
/** @var AbstractDNSWriter $_dnsWriter */
|
||||
private static $_dnsWriter = null;
|
||||
|
||||
public static function setWriter(AbstractDNSWriter $dnsWriter) : void {
|
||||
self::$_dnsWriter = $dnsWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response\Authorization\Struct\Challenge $challenge
|
||||
* @param Response\Authorization\Get $authorizationResponse
|
||||
* @return bool
|
||||
*
|
||||
* @throws Exception\AuthorizationInvalid
|
||||
* @throws Exception\DNSAuthorizationInvalid
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
protected function _existsNotValidChallenges(Response\Authorization\Struct\Challenge $challenge,
|
||||
Response\Authorization\Get $authorizationResponse
|
||||
) : bool {
|
||||
|
||||
if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_PENDING) {
|
||||
|
||||
if(self::$_dnsWriter === null) {
|
||||
throw new \RuntimeException('DNS writer is not set');
|
||||
}
|
||||
|
||||
if( self::$_dnsWriter->write(
|
||||
$this->_order,
|
||||
$authorizationResponse->getIdentifier()->value,
|
||||
(new ChallengeAuthorizationKey($this->_account))->getEncoded($challenge->token)
|
||||
)
|
||||
) {
|
||||
$request = new Request\Authorization\Start($this->_account, $this->_order, $challenge);
|
||||
/* $response = */ $request->getResponse();
|
||||
} else {
|
||||
|
||||
Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO, 'Pending challenge deferred');
|
||||
}
|
||||
}
|
||||
|
||||
if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_INVALID) {
|
||||
throw new Exception\DNSAuthorizationInvalid(
|
||||
'Received status "' . Response\Authorization\Struct\Challenge::STATUS_INVALID . '" while challenge should be verified'
|
||||
);
|
||||
}
|
||||
|
||||
return parent::_existsNotValidChallenges($challenge, $authorizationResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Authorizer;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Struct\ChallengeAuthorizationKey;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Order;
|
||||
|
||||
class HTTP extends AbstractAuthorizer {
|
||||
|
||||
protected static $_directoryPath = null;
|
||||
|
||||
public static function setDirectoryPath(string $directoryPath) {
|
||||
|
||||
if(!file_exists($directoryPath)) {
|
||||
throw new \RuntimeException('HTTP authorization directory path does not exist');
|
||||
}
|
||||
|
||||
self::$_directoryPath = realpath($directoryPath) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
public static function getDirectoryPath() : ?string {
|
||||
return self::$_directoryPath;
|
||||
}
|
||||
|
||||
protected function _getChallengeType(): string {
|
||||
return Order::CHALLENGE_TYPE_HTTP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response\Authorization\Struct\Challenge $challenge
|
||||
* @param Response\Authorization\Get $authorizationResponse
|
||||
* @return bool
|
||||
*
|
||||
* @throws Exception\AuthorizationInvalid
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
protected function _existsNotValidChallenges(Response\Authorization\Struct\Challenge $challenge,
|
||||
Response\Authorization\Get $authorizationResponse
|
||||
) : bool {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
'Challenge "' . $challenge->token . '" has status:' . $challenge->status
|
||||
);
|
||||
|
||||
if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_PENDING) {
|
||||
|
||||
$this->_writeToFile($challenge);
|
||||
if($this->_validateFile($authorizationResponse->getIdentifier()->value, $challenge)) {
|
||||
|
||||
$request = new Request\Authorization\Start($this->_account, $this->_order, $challenge);
|
||||
/* $response = */ $request->getResponse();
|
||||
} else {
|
||||
|
||||
Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO, 'Could not validate HTTP Authorization file');
|
||||
}
|
||||
}
|
||||
|
||||
if($challenge->status == Response\Authorization\Struct\Challenge::STATUS_INVALID) {
|
||||
throw new Exception\HTTPAuthorizationInvalid(
|
||||
'Received status "' . Response\Authorization\Struct\Challenge::STATUS_INVALID . '" while challenge should be verified'
|
||||
);
|
||||
}
|
||||
|
||||
return parent::_existsNotValidChallenges($challenge, $authorizationResponse);
|
||||
}
|
||||
|
||||
private function _writeToFile(Response\Authorization\Struct\Challenge $challenge) : void {
|
||||
|
||||
file_put_contents(
|
||||
self::$_directoryPath . $challenge->token,
|
||||
(new ChallengeAuthorizationKey($this->_account))->get($challenge->token)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $domain
|
||||
* @param Response\Authorization\Struct\Challenge $challenge
|
||||
* @return bool
|
||||
*
|
||||
* @throws Exception\HTTPAuthorizationInvalid
|
||||
*/
|
||||
private function _validateFile(string $domain, Response\Authorization\Struct\Challenge $challenge) : bool {
|
||||
|
||||
if ( get_option('rsssl_skip_challenge_directory_request') ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$challengeAuthorizationKey = new ChallengeAuthorizationKey($this->_account);
|
||||
|
||||
$requestURL = 'http://' . $domain . '/.well-known/acme-challenge/' . $challenge->token;
|
||||
$handle = curl_init();
|
||||
curl_setopt($handle, CURLOPT_URL, $requestURL);
|
||||
curl_setopt($handle, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
|
||||
$response = curl_exec($handle);
|
||||
|
||||
$result = !empty($response) && $response == $challengeAuthorizationKey->get($challenge->token);
|
||||
|
||||
if(!$result) {
|
||||
|
||||
throw new Exception\HTTPAuthorizationInvalid(
|
||||
'HTTP challenge for "' . $domain . '"": ' .
|
||||
$domain . '/.well-known/acme-challenge/' . $challenge->token .
|
||||
' tested, found invalid. CURL response: ' . var_export($response, true)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace LE_ACME2\Cache;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\AbstractKeyValuable;
|
||||
|
||||
abstract class AbstractKeyValuableCache {
|
||||
|
||||
protected function __construct() {}
|
||||
|
||||
protected function _getObjectIdentifier(AbstractKeyValuable $object) : string {
|
||||
return $object->getKeyDirectoryPath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace LE_ACME2\Cache;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Account;
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\SingletonTrait;
|
||||
|
||||
class AccountResponse extends AbstractKeyValuableCache {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
private const _FILE = 'CacheResponse';
|
||||
private const _DEPRECATED_FILE = 'DirectoryNewAccountResponse';
|
||||
|
||||
private $_responses = [];
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return Response\Account\AbstractAccount
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function get(Account $account): Response\Account\AbstractAccount {
|
||||
|
||||
$accountIdentifier = $this->_getObjectIdentifier($account);
|
||||
|
||||
if(array_key_exists($accountIdentifier, $this->_responses)) {
|
||||
return $this->_responses[ $accountIdentifier ];
|
||||
}
|
||||
$this->_responses[ $accountIdentifier ] = null;
|
||||
|
||||
$cacheFile = $account->getKeyDirectoryPath() . self::_FILE;
|
||||
$deprecatedCacheFile = $account->getKeyDirectoryPath() . self::_DEPRECATED_FILE;
|
||||
|
||||
if(file_exists($deprecatedCacheFile) && !file_exists($cacheFile)) {
|
||||
rename($deprecatedCacheFile, $cacheFile);
|
||||
}
|
||||
|
||||
if(file_exists($cacheFile) && filemtime($cacheFile) > strtotime('-7 days')) {
|
||||
|
||||
$rawResponse = Connector\RawResponse::getFromString(file_get_contents($cacheFile));
|
||||
|
||||
$response = new Response\Account\Create($rawResponse);
|
||||
|
||||
$this->_responses[ $accountIdentifier ] = $response;
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' response from cache'
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$request = new Request\Account\Get($account);
|
||||
$response = $request->getResponse();
|
||||
|
||||
$this->set($account, $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function set(Account $account, Response\Account\AbstractAccount $response = null) : void {
|
||||
|
||||
$accountIdentifier = $this->_getObjectIdentifier($account);
|
||||
|
||||
$filePath = $account->getKeyDirectoryPath() . self::_FILE;
|
||||
|
||||
if($response === null) {
|
||||
|
||||
unset($this->_responses[$accountIdentifier]);
|
||||
|
||||
if(file_exists($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_responses[$accountIdentifier] = $response;
|
||||
file_put_contents($filePath, $response->getRaw()->toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace LE_ACME2\Cache;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
use LE_ACME2\SingletonTrait;
|
||||
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
class DirectoryResponse {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
private const _FILE = 'DirectoryResponse';
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
private $_responses = [];
|
||||
private $_index = 0;
|
||||
|
||||
/**
|
||||
* @return Response\GetDirectory
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function get() : Response\GetDirectory {
|
||||
|
||||
if(array_key_exists($this->_index, $this->_responses)) {
|
||||
return $this->_responses[$this->_index];
|
||||
}
|
||||
$this->_responses[$this->_index] = null;
|
||||
|
||||
$cacheFile = Account::getCommonKeyDirectoryPath() . self::_FILE;
|
||||
|
||||
if(file_exists($cacheFile) && filemtime($cacheFile) > strtotime('-2 days')) {
|
||||
|
||||
$rawResponse = Connector\RawResponse::getFromString(file_get_contents($cacheFile));
|
||||
|
||||
try {
|
||||
return $this->_responses[$this->_index] = new Response\GetDirectory($rawResponse);
|
||||
|
||||
} catch(Exception\AbstractException $e) {
|
||||
unlink($cacheFile);
|
||||
}
|
||||
}
|
||||
|
||||
$request = new Request\GetDirectory();
|
||||
$response = $request->getResponse();
|
||||
$this->set($response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function set(Response\GetDirectory $response) : void {
|
||||
|
||||
$cacheFile = Account::getCommonKeyDirectoryPath() . self::_FILE;
|
||||
|
||||
$this->_responses[$this->_index] = $response;
|
||||
file_put_contents($cacheFile, $response->getRaw()->toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace LE_ACME2\Cache;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\SingletonTrait;
|
||||
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
class NewNonceResponse {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
private $_responses = [];
|
||||
private $_index = 0;
|
||||
|
||||
/**
|
||||
* @return Response\GetNewNonce
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function get() : Response\GetNewNonce {
|
||||
|
||||
if(array_key_exists($this->_index, $this->_responses)) {
|
||||
return $this->_responses[$this->_index];
|
||||
}
|
||||
$this->_responses[$this->_index] = null;
|
||||
|
||||
$request = new Request\GetNewNonce();
|
||||
$response = $request->getResponse();
|
||||
$this->set($response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function set(Response\GetNewNonce $response) : void {
|
||||
$this->_responses[$this->_index] = $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
namespace LE_ACME2\Cache;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Order;
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\SingletonTrait;
|
||||
|
||||
class OrderResponse extends AbstractKeyValuableCache {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
private const _FILE = 'CacheResponse';
|
||||
private const _DEPRECATED_FILE = 'DirectoryNewOrderResponse';
|
||||
|
||||
private $_responses = [];
|
||||
|
||||
public function exists(Order $order) : bool {
|
||||
|
||||
$cacheFile = $order->getKeyDirectoryPath() . self::_FILE;
|
||||
$deprecatedCacheFile = $order->getKeyDirectoryPath() . self::_DEPRECATED_FILE;
|
||||
|
||||
return file_exists($cacheFile) || file_exists($deprecatedCacheFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Order $order
|
||||
* @return Response\Order\AbstractOrder
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function get(Order $order): Response\Order\AbstractOrder {
|
||||
|
||||
$accountIdentifier = $this->_getObjectIdentifier($order->getAccount());
|
||||
$orderIdentifier = $this->_getObjectIdentifier($order);
|
||||
|
||||
if(!isset($this->_responses[$accountIdentifier])) {
|
||||
$this->_responses[$accountIdentifier] = [];
|
||||
}
|
||||
|
||||
if(array_key_exists($orderIdentifier, $this->_responses[$accountIdentifier])) {
|
||||
return $this->_responses[ $accountIdentifier ][ $orderIdentifier ];
|
||||
}
|
||||
$this->_responses[ $accountIdentifier ][ $orderIdentifier ] = null;
|
||||
|
||||
$cacheFile = $order->getKeyDirectoryPath() . self::_FILE;
|
||||
$deprecatedCacheFile = $order->getKeyDirectoryPath() . self::_DEPRECATED_FILE;
|
||||
|
||||
if(file_exists($deprecatedCacheFile) && !file_exists($cacheFile)) {
|
||||
rename($deprecatedCacheFile, $cacheFile);
|
||||
}
|
||||
|
||||
if(file_exists($cacheFile)) {
|
||||
|
||||
$rawResponse = Connector\RawResponse::getFromString(file_get_contents($cacheFile));
|
||||
|
||||
$response = new Response\Order\Create($rawResponse);
|
||||
|
||||
if(
|
||||
$response->getStatus() != Response\Order\AbstractOrder::STATUS_VALID
|
||||
) {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' (cache did not satisfy, status "' . $response->getStatus() . '")'
|
||||
);
|
||||
|
||||
$request = new Request\Order\Get($order, $response);
|
||||
$response = $request->getResponse();
|
||||
$this->set($order, $response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' (from cache, status "' . $response->getStatus() . '")'
|
||||
);
|
||||
|
||||
$this->_responses[$accountIdentifier][$orderIdentifier] = $response;
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
self::_FILE . ' could not be found for order: ' .
|
||||
'- Path: ' . $order->getKeyDirectoryPath() . PHP_EOL .
|
||||
'- Subjects: ' . var_export($order->getSubjects(), true) . PHP_EOL
|
||||
);
|
||||
}
|
||||
|
||||
public function set(Order $order, Response\Order\AbstractOrder $response = null) : void {
|
||||
|
||||
$accountIdentifier = $this->_getObjectIdentifier($order->getAccount());
|
||||
$orderIdentifier = $this->_getObjectIdentifier($order);
|
||||
|
||||
$filePath = $order->getKeyDirectoryPath() . self::_FILE;
|
||||
|
||||
if($response === null) {
|
||||
|
||||
unset($this->_responses[$accountIdentifier][$orderIdentifier]);
|
||||
|
||||
if(file_exists($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_responses[$accountIdentifier][$orderIdentifier] = $response;
|
||||
file_put_contents($filePath, $response->getRaw()->toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Connector;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\SingletonTrait;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class Connector {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
const METHOD_GET = 'GET';
|
||||
const METHOD_HEAD = 'HEAD';
|
||||
const METHOD_POST = 'POST';
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
protected $_baseURL = 'https://acme-v02.api.letsencrypt.org';
|
||||
protected $_stagingBaseURL = 'https://acme-staging-v02.api.letsencrypt.org';
|
||||
|
||||
protected $_useStagingServer = true;
|
||||
|
||||
public function useStagingServer(bool $useStagingServer) {
|
||||
$this->_useStagingServer = $useStagingServer;
|
||||
}
|
||||
|
||||
public function isUsingStagingServer() : bool {
|
||||
return $this->_useStagingServer;
|
||||
}
|
||||
|
||||
public function getBaseURL() : string {
|
||||
return $this->_useStagingServer ? $this->_stagingBaseURL : $this->_baseURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a Curl request.
|
||||
*
|
||||
* @param string $method The HTTP method to use. Accepting GET, POST and HEAD requests.
|
||||
* @param string $url The URL to make the request to.
|
||||
* @param string $data The body to attach to a POST request. Expected as a JSON encoded string.
|
||||
*
|
||||
* @return RawResponse
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function request(string $method, string $url, string $data = null) : RawResponse {
|
||||
|
||||
Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO, 'will request from ' . $url, $data);
|
||||
|
||||
$handle = curl_init();
|
||||
|
||||
$headers = array(
|
||||
'Accept: application/json',
|
||||
'Content-Type: ' . ($method == self::METHOD_POST ? 'application/jose+json' : 'application/json') // ACME draft-10, section 6.2
|
||||
);
|
||||
|
||||
curl_setopt($handle, CURLOPT_URL, $url);
|
||||
curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($handle, CURLOPT_HEADER, true);
|
||||
|
||||
switch ($method) {
|
||||
case self::METHOD_GET:
|
||||
break;
|
||||
case self::METHOD_POST:
|
||||
curl_setopt($handle, CURLOPT_POST, true);
|
||||
curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
|
||||
break;
|
||||
case self::METHOD_HEAD:
|
||||
curl_setopt($handle, CURLOPT_CUSTOMREQUEST, 'HEAD');
|
||||
curl_setopt($handle, CURLOPT_NOBODY, true);
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException('HTTP request ' . $method . ' not supported.');
|
||||
break;
|
||||
}
|
||||
$response = curl_exec($handle);
|
||||
|
||||
if(curl_errno($handle)) {
|
||||
throw new \RuntimeException('Curl: ' . curl_error($handle));
|
||||
}
|
||||
|
||||
$header_size = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
|
||||
|
||||
$rawResponse = new RawResponse();
|
||||
$rawResponse->init($method, $url, $response, $header_size);
|
||||
|
||||
Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO, self::class . ': response received', $rawResponse);
|
||||
|
||||
|
||||
try {
|
||||
$getNewNonceResponse = new Response\GetNewNonce($rawResponse);
|
||||
Cache\NewNonceResponse::getInstance()->set($getNewNonceResponse);
|
||||
|
||||
} catch(Exception\InvalidResponse $e) {
|
||||
|
||||
if($method == self::METHOD_POST) {
|
||||
$request = new Request\GetNewNonce();
|
||||
Cache\NewNonceResponse::getInstance()->set($request->getResponse());
|
||||
}
|
||||
}
|
||||
|
||||
return $rawResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Connector;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class RawResponse {
|
||||
|
||||
/** @var string */
|
||||
public $request;
|
||||
|
||||
/** @var array */
|
||||
public $header;
|
||||
|
||||
/** @var array|string */
|
||||
public $body;
|
||||
|
||||
public function init(string $method, string $url, string $response, int $headerSize) {
|
||||
|
||||
$header = substr($response, 0, $headerSize);
|
||||
$body = substr($response, $headerSize);
|
||||
|
||||
$body_json = json_decode($body, true);
|
||||
|
||||
$this->request = $method . ' ' . $url;
|
||||
|
||||
$this->header = array_map(function($line) {
|
||||
return trim($line);
|
||||
}, explode("\n", $header));
|
||||
|
||||
$this->body = $body_json === null ? $body : $body_json;
|
||||
}
|
||||
|
||||
public function toString() : string {
|
||||
|
||||
return serialize([
|
||||
'request' => $this->request,
|
||||
'header' => $this->header,
|
||||
'body' => $this->body,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getFromString(string $string) : self {
|
||||
|
||||
$array = unserialize($string);
|
||||
|
||||
$rawResponse = new self();
|
||||
|
||||
$rawResponse->request = $array['request'];
|
||||
$rawResponse->header = $array['header'];
|
||||
$rawResponse->body = $array['body'];
|
||||
|
||||
return $rawResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
abstract class AbstractException extends \Exception {
|
||||
|
||||
public function __construct(string $message) {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
'Exception "' . get_called_class() . '" thrown '
|
||||
);
|
||||
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class AuthorizationInvalid extends AbstractException {}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class DNSAuthorizationInvalid extends AuthorizationInvalid {}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class ExpiredAuthorization extends AbstractException {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct("Expired authorization received");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class HTTPAuthorizationInvalid extends AuthorizationInvalid {}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Connector\RawResponse;
|
||||
|
||||
class InvalidResponse extends AbstractException {
|
||||
|
||||
private $_rawResponse;
|
||||
private $_responseStatus;
|
||||
|
||||
public function __construct(RawResponse $rawResponse, string $responseStatus = null) {
|
||||
|
||||
$this->_rawResponse = $rawResponse;
|
||||
$this->_responseStatus = $responseStatus;
|
||||
|
||||
if($responseStatus === '') {
|
||||
$responseStatus = 'Unknown response status';
|
||||
}
|
||||
|
||||
if(isset($this->_rawResponse->body['type'])) {
|
||||
$responseStatus = $this->_rawResponse->body['type'];
|
||||
}
|
||||
|
||||
if(isset($this->_rawResponse->body['detail'])) {
|
||||
$responseStatus .= ' - ' . $this->_rawResponse->body['detail'];
|
||||
}
|
||||
|
||||
parent::__construct('Invalid response received: ' . $responseStatus);
|
||||
}
|
||||
|
||||
public function getRawResponse() : RawResponse {
|
||||
return $this->_rawResponse;
|
||||
}
|
||||
|
||||
public function getResponseStatus() : ?string {
|
||||
return $this->_responseStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class OpenSSLException extends AbstractException {
|
||||
|
||||
public function __construct(string $function) {
|
||||
|
||||
$errors = [];
|
||||
while(($error = openssl_error_string()) !== false) {
|
||||
$errors[] = $error;
|
||||
}
|
||||
|
||||
parent::__construct(
|
||||
$function . ' failed - error messages: ' . var_export($errors, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class RateLimitReached extends AbstractException {
|
||||
|
||||
public function __construct(string $request, string $detail) {
|
||||
parent::__construct(
|
||||
"Invalid response received for request (" . $request . "): " .
|
||||
"rate limit reached - " . $detail
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Exception;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class StatusInvalid extends AbstractException {}
|
||||
438
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/src/LE_ACME2/Order.php
vendored
Normal file
438
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/fbett/le_acme2/src/LE_ACME2/Order.php
vendored
Normal file
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Authorizer;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
class Order extends AbstractKeyValuable {
|
||||
|
||||
const CHALLENGE_TYPE_HTTP = 'http-01';
|
||||
const CHALLENGE_TYPE_DNS = 'dns-01';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param $directoryPath
|
||||
*/
|
||||
public static function setHTTPAuthorizationDirectoryPath(string $directoryPath) {
|
||||
|
||||
Authorizer\HTTP::setDirectoryPath($directoryPath);
|
||||
}
|
||||
|
||||
CONST IDENTRUST_ISSUER_CN = 'DST Root CA X3';
|
||||
|
||||
/** @var string|null $_preferredChain */
|
||||
private static $_preferredChain = null;
|
||||
|
||||
public static function setPreferredChain(string $issuerCN = null) {
|
||||
self::$_preferredChain = $issuerCN;
|
||||
}
|
||||
|
||||
protected $_account;
|
||||
protected $_subjects;
|
||||
|
||||
public function __construct(Account $account, array $subjects) {
|
||||
|
||||
array_map(function($subject) {
|
||||
|
||||
if(preg_match_all('~(\*\.)~', $subject) > 1)
|
||||
throw new \RuntimeException('Cannot create orders with multiple wildcards in one domain.');
|
||||
|
||||
}, $subjects);
|
||||
|
||||
$this->_account = $account;
|
||||
$this->_subjects = $subjects;
|
||||
|
||||
$this->_identifier = $this->_getAccountIdentifier($account) . DIRECTORY_SEPARATOR .
|
||||
'order_' . md5(implode('|', $subjects));
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_INFO,
|
||||
get_class() . '::' . __FUNCTION__ . ' "' . implode(':', $this->getSubjects()) . '"'
|
||||
);
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_DEBUG,
|
||||
get_class() . '::' . __FUNCTION__ . ' path: ' . $this->getKeyDirectoryPath()
|
||||
);
|
||||
}
|
||||
|
||||
public function getAccount() : Account {
|
||||
return $this->_account;
|
||||
}
|
||||
|
||||
public function getSubjects() : array {
|
||||
|
||||
return $this->_subjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param array $subjects
|
||||
* @param string $keyType
|
||||
* @return Order
|
||||
* @throws Exception\AbstractException
|
||||
*/
|
||||
public static function create(Account $account, array $subjects, string $keyType = self::KEY_TYPE_RSA) : Order {
|
||||
|
||||
$order = new self($account, $subjects);
|
||||
return $order->_create($keyType, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $keyType
|
||||
* @param bool $ignoreIfKeysExist
|
||||
* @return Order
|
||||
* @throws Exception\AbstractException
|
||||
*/
|
||||
protected function _create(string $keyType, bool $ignoreIfKeysExist = false) : Order {
|
||||
|
||||
$this->_initKeyDirectory($keyType, $ignoreIfKeysExist);
|
||||
|
||||
$request = new Request\Order\Create($this);
|
||||
|
||||
try {
|
||||
$response = $request->getResponse();
|
||||
|
||||
Cache\OrderResponse::getInstance()->set($this, $response);
|
||||
return $this;
|
||||
|
||||
} catch(Exception\AbstractException $e) {
|
||||
$this->_clearKeyDirectory();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public static function exists(Account $account, array $subjects) : bool {
|
||||
|
||||
$order = new self($account, $subjects);
|
||||
return Cache\OrderResponse::getInstance()->exists($order);
|
||||
}
|
||||
|
||||
public static function get(Account $account, array $subjects) : Order {
|
||||
|
||||
$order = new self($account, $subjects);
|
||||
|
||||
if(!self::exists($account, $subjects))
|
||||
throw new \RuntimeException('Order does not exist');
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/** @var Authorizer\AbstractAuthorizer|Authorizer\HTTP|null $_authorizer */
|
||||
protected $_authorizer = null;
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @return Authorizer\AbstractAuthorizer|Authorizer\HTTP|null
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
protected function _getAuthorizer(string $type) : Authorizer\AbstractAuthorizer {
|
||||
|
||||
if($this->_authorizer === null) {
|
||||
|
||||
if($type == self::CHALLENGE_TYPE_HTTP) {
|
||||
$this->_authorizer = new Authorizer\HTTP($this->_account, $this);
|
||||
} else if($type == self::CHALLENGE_TYPE_DNS) {
|
||||
$this->_authorizer = new Authorizer\DNS($this->_account, $this);
|
||||
} else {
|
||||
throw new \RuntimeException('Challenge type not implemented');
|
||||
}
|
||||
}
|
||||
return $this->_authorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Authorization has expired, so we clean the complete order to restart again on the next call
|
||||
*/
|
||||
protected function _clearAfterExpiredAuthorization() {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_INFO,
|
||||
get_class() . '::' . __FUNCTION__ . ' "Will clear after expired authorization'
|
||||
);
|
||||
|
||||
$this->clear();
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
Cache\OrderResponse::getInstance()->set($this, null);
|
||||
$this->_clearKeyDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param $type
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function shouldStartAuthorization(string $type) : bool {
|
||||
|
||||
try {
|
||||
return $this->_getAuthorizer($type)->shouldStartAuthorization();
|
||||
} catch(Exception\ExpiredAuthorization $e) {
|
||||
|
||||
$this->_clearAfterExpiredAuthorization();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @return bool
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\AuthorizationInvalid
|
||||
*/
|
||||
public function authorize(string $type) : bool {
|
||||
|
||||
try {
|
||||
$authorizer = $this->_getAuthorizer($type);
|
||||
$authorizer->progress();
|
||||
|
||||
return $authorizer->hasFinished();
|
||||
} catch(Exception\ExpiredAuthorization $e) {
|
||||
|
||||
$this->_clearAfterExpiredAuthorization();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function finalize() {
|
||||
|
||||
if(!is_object($this->_authorizer) || !$this->_authorizer->hasFinished()) {
|
||||
|
||||
throw new \RuntimeException('Not all challenges are valid. Please check result of authorize() first!');
|
||||
}
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_INFO,
|
||||
get_class() . '::' . __FUNCTION__ . ' "Will finalize'
|
||||
);
|
||||
|
||||
$orderResponse = Cache\OrderResponse::getInstance()->get($this);
|
||||
|
||||
if(
|
||||
$orderResponse->getStatus() == Response\Order\AbstractOrder::STATUS_PENDING /* DEPRECATED AFTER JULI 5TH 2018 */ ||
|
||||
$orderResponse->getStatus() == Response\Order\AbstractOrder::STATUS_READY // ACME draft-12 Section 7.1.6
|
||||
) {
|
||||
|
||||
$request = new Request\Order\Finalize($this, $orderResponse);
|
||||
$orderResponse = $request->getResponse();
|
||||
Cache\OrderResponse::getInstance()->set($this, $orderResponse);
|
||||
}
|
||||
|
||||
if($orderResponse->getStatus() == Response\Order\AbstractOrder::STATUS_VALID) {
|
||||
|
||||
$request = new Request\Order\GetCertificate($this, $orderResponse);
|
||||
$response = $request->getResponse();
|
||||
|
||||
$certificate = $response->getCertificate();
|
||||
$intermediate = $response->getIntermediate();
|
||||
|
||||
//$certificateInfo = openssl_x509_parse($certificate);
|
||||
//$certificateValidToTimeTimestamp = $certificateInfo['validTo_time_t'];
|
||||
$intermediateInfo = openssl_x509_parse($intermediate);
|
||||
|
||||
if(self::$_preferredChain !== null) {
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_INFO,
|
||||
'Preferred chain is set: ' . self::$_preferredChain
|
||||
);
|
||||
}
|
||||
|
||||
$found = false;
|
||||
if(self::$_preferredChain !== null && $intermediateInfo['issuer']['CN'] != self::$_preferredChain) {
|
||||
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_INFO,
|
||||
'Default certificate does not satisfy preferred chain, trying to fetch alternative'
|
||||
);
|
||||
|
||||
foreach($response->getAlternativeLinks() as $link) {
|
||||
|
||||
$request = new Request\Order\GetCertificate($this, $orderResponse, $link);
|
||||
$response = $request->getResponse();
|
||||
|
||||
$alternativeCertificate = $response->getCertificate();
|
||||
$alternativeIntermediate = $response->getIntermediate();
|
||||
|
||||
$intermediateInfo = openssl_x509_parse($intermediate);
|
||||
if($intermediateInfo['issuer']['CN'] != self::$_preferredChain) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$found = true;
|
||||
|
||||
$certificate = $alternativeCertificate;
|
||||
$intermediate = $alternativeIntermediate;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(!$found) {
|
||||
Utilities\Logger::getInstance()->add(
|
||||
Utilities\Logger::LEVEL_INFO,
|
||||
'Preferred chain could not be satisfied, returning default chain'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_saveCertificate($certificate, $intermediate);
|
||||
}
|
||||
}
|
||||
|
||||
private function _saveCertificate(string $certificate, string $intermediate) : void {
|
||||
|
||||
$certificateInfo = openssl_x509_parse($certificate);
|
||||
$certificateValidToTimeTimestamp = $certificateInfo['validTo_time_t'];
|
||||
|
||||
$path = $this->getKeyDirectoryPath() . self::BUNDLE_DIRECTORY_PREFIX . $certificateValidToTimeTimestamp . DIRECTORY_SEPARATOR;
|
||||
|
||||
mkdir($path);
|
||||
rename($this->getKeyDirectoryPath() . 'private.pem', $path . 'private.pem');
|
||||
file_put_contents($path . 'certificate.crt', $certificate);
|
||||
file_put_contents($path . 'intermediate.pem', $intermediate);
|
||||
|
||||
Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO, 'Certificate received');
|
||||
}
|
||||
|
||||
const BUNDLE_DIRECTORY_PREFIX = 'bundle_';
|
||||
|
||||
protected function _getLatestCertificateDirectory() : ?string {
|
||||
|
||||
$files = scandir($this->getKeyDirectoryPath(), SORT_NUMERIC | SORT_DESC);
|
||||
foreach($files as $file) {
|
||||
if(
|
||||
substr($file, 0, strlen(self::BUNDLE_DIRECTORY_PREFIX)) == self::BUNDLE_DIRECTORY_PREFIX &&
|
||||
is_dir($this->getKeyDirectoryPath() . $file)
|
||||
) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isCertificateBundleAvailable() : bool {
|
||||
|
||||
return $this->_getLatestCertificateDirectory() !== NULL;
|
||||
}
|
||||
|
||||
public function getCertificateBundle() : Struct\CertificateBundle {
|
||||
|
||||
if(!$this->isCertificateBundleAvailable()) {
|
||||
throw new \RuntimeException('There is no certificate available');
|
||||
}
|
||||
|
||||
$certificatePath = $this->getKeyDirectoryPath() . $this->_getLatestCertificateDirectory();
|
||||
|
||||
return new Struct\CertificateBundle(
|
||||
$certificatePath . DIRECTORY_SEPARATOR,
|
||||
'private.pem',
|
||||
'certificate.crt',
|
||||
'intermediate.pem',
|
||||
self::_getExpireTimeFromCertificateDirectoryPath($certificatePath)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $keyType
|
||||
* @param int|null $renewBefore Unix timestamp
|
||||
* @throws Exception\AbstractException
|
||||
*/
|
||||
public function enableAutoRenewal($keyType = self::KEY_TYPE_RSA, int $renewBefore = null) {
|
||||
|
||||
if($keyType === null) {
|
||||
$keyType = self::KEY_TYPE_RSA;
|
||||
}
|
||||
|
||||
if(!$this->isCertificateBundleAvailable()) {
|
||||
throw new \RuntimeException('There is no certificate available');
|
||||
}
|
||||
|
||||
$orderResponse = Cache\OrderResponse::getInstance()->get($this);
|
||||
if(
|
||||
$orderResponse === null ||
|
||||
$orderResponse->getStatus() != Response\Order\AbstractOrder::STATUS_VALID
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_DEBUG,'Auto renewal triggered');
|
||||
|
||||
$directory = $this->_getLatestCertificateDirectory();
|
||||
|
||||
$expireTime = self::_getExpireTimeFromCertificateDirectoryPath($directory);
|
||||
|
||||
if($renewBefore === null) {
|
||||
$renewBefore = strtotime('-30 days', $expireTime);
|
||||
}
|
||||
|
||||
if($renewBefore < time()) {
|
||||
|
||||
Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO,'Auto renewal: Will recreate order');
|
||||
|
||||
$this->_create($keyType, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $reason The reason to revoke the LetsEncrypt Order instance certificate.
|
||||
* Possible reasons can be found in section 5.3.1 of RFC5280.
|
||||
* @return bool
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function revokeCertificate(int $reason = 0) : bool {
|
||||
|
||||
if(!$this->isCertificateBundleAvailable()) {
|
||||
throw new \RuntimeException('There is no certificate available to revoke');
|
||||
}
|
||||
|
||||
$bundle = $this->getCertificateBundle();
|
||||
|
||||
$request = new Request\Order\RevokeCertificate($bundle, $reason);
|
||||
|
||||
try {
|
||||
/* $response = */ $request->getResponse();
|
||||
rename(
|
||||
$this->getKeyDirectoryPath(),
|
||||
$this->_getKeyDirectoryPath('-revoked-' . microtime(true))
|
||||
);
|
||||
return true;
|
||||
} catch(Exception\InvalidResponse $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected static function _getExpireTimeFromCertificateDirectoryPath(string $path) {
|
||||
|
||||
$stringPosition = strrpos($path, self::BUNDLE_DIRECTORY_PREFIX);
|
||||
if($stringPosition === false) {
|
||||
throw new \RuntimeException('ExpireTime not found in' . $path);
|
||||
}
|
||||
|
||||
$expireTime = substr($path, $stringPosition + strlen(self::BUNDLE_DIRECTORY_PREFIX));
|
||||
if(
|
||||
!is_numeric($expireTime) ||
|
||||
$expireTime < strtotime('-10 years') ||
|
||||
$expireTime > strtotime('+10 years')
|
||||
) {
|
||||
throw new \RuntimeException('Unexpected expireTime: ' . $expireTime);
|
||||
}
|
||||
return $expireTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
abstract class AbstractRequest {
|
||||
|
||||
/**
|
||||
* @return AbstractResponse
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
abstract public function getResponse() : AbstractResponse;
|
||||
|
||||
protected function _buildContactPayload(string $email) : array {
|
||||
|
||||
$result = [
|
||||
'mailto:' . $email
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
|
||||
abstract class AbstractLocation extends AbstractRequest {
|
||||
|
||||
protected $_account;
|
||||
|
||||
public function __construct(Account $account) {
|
||||
$this->_account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Connector\RawResponse
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
protected function _getRawResponse() : Connector\RawResponse {
|
||||
|
||||
$payload = $this->_getPayload();
|
||||
if(count($payload) == 0) {
|
||||
$payload['rand-' . rand(100000, 1000000)] = 1;
|
||||
}
|
||||
|
||||
$kid = Utilities\RequestSigner::KID(
|
||||
$payload,
|
||||
Cache\AccountResponse::getInstance()->get($this->_account)->getLocation(),
|
||||
Cache\AccountResponse::getInstance()->get($this->_account)->getLocation(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_account->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
Cache\AccountResponse::getInstance()->get($this->_account)->getLocation(),
|
||||
$kid
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
abstract protected function _getPayload() : array;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
|
||||
class ChangeKeys extends AbstractRequest {
|
||||
|
||||
protected $_account;
|
||||
|
||||
public function __construct(Account $account) {
|
||||
$this->_account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Account\ChangeKeys
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$currentPrivateKey = openssl_pkey_get_private(
|
||||
file_get_contents($this->_account->getKeyDirectoryPath() . 'private.pem')
|
||||
);
|
||||
$currentPrivateKeyDetails = openssl_pkey_get_details($currentPrivateKey);
|
||||
|
||||
/**
|
||||
* draft-13 Section 7.3.6
|
||||
* "newKey" is deprecated after August 23rd 2018
|
||||
*/
|
||||
$newPrivateKey = openssl_pkey_get_private(
|
||||
file_get_contents($this->_account->getKeyDirectoryPath() . 'private-replacement.pem')
|
||||
);
|
||||
$newPrivateKeyDetails = openssl_pkey_get_details($newPrivateKey);
|
||||
|
||||
$innerPayload = [
|
||||
'account' => Cache\AccountResponse::getInstance()->get($this->_account)->getLocation(),
|
||||
'oldKey' => [
|
||||
"kty" => "RSA",
|
||||
"n" => Utilities\Base64::UrlSafeEncode($currentPrivateKeyDetails["rsa"]["n"]),
|
||||
"e" => Utilities\Base64::UrlSafeEncode($currentPrivateKeyDetails["rsa"]["e"])
|
||||
],
|
||||
'newKey' => [
|
||||
"kty" => "RSA",
|
||||
"n" => Utilities\Base64::UrlSafeEncode($newPrivateKeyDetails["rsa"]["n"]),
|
||||
"e" => Utilities\Base64::UrlSafeEncode($newPrivateKeyDetails["rsa"]["e"])
|
||||
]
|
||||
];
|
||||
|
||||
$outerPayload = Utilities\RequestSigner::JWK(
|
||||
$innerPayload,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getKeyChange(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_account->getKeyDirectoryPath(),
|
||||
'private-replacement.pem'
|
||||
);
|
||||
|
||||
$data = Utilities\RequestSigner::KID(
|
||||
$outerPayload,
|
||||
Cache\AccountResponse::getInstance()->get($this->_account)->getLocation(),
|
||||
Cache\DirectoryResponse::getInstance()->get()->getKeyChange(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_account->getKeyDirectoryPath(),
|
||||
'private.pem'
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getKeyChange(),
|
||||
$data
|
||||
);
|
||||
|
||||
return new Response\Account\ChangeKeys($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Utilities;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
|
||||
class Create extends AbstractRequest {
|
||||
|
||||
protected $_account;
|
||||
|
||||
public function __construct(Account $account) {
|
||||
$this->_account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Account\Create
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$payload = [
|
||||
'contact' => $this->_buildContactPayload($this->_account->getEmail()),
|
||||
'termsOfServiceAgreed' => true,
|
||||
];
|
||||
|
||||
$jwk = Utilities\RequestSigner::JWKString(
|
||||
$payload,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getNewAccount(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_account->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getNewAccount(),
|
||||
$jwk
|
||||
);
|
||||
|
||||
return new Response\Account\Create($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class Deactivate extends AbstractLocation {
|
||||
|
||||
protected function _getPayload() : array {
|
||||
|
||||
return [
|
||||
'status' => 'deactivated',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Account\Deactivate
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
return new Response\Account\Deactivate($this->_getRawResponse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
|
||||
class Get extends AbstractRequest {
|
||||
|
||||
protected $_account;
|
||||
|
||||
public function __construct(Account $account) {
|
||||
$this->_account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Account\Get
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$payload = [
|
||||
'onlyReturnExisting' => true,
|
||||
];
|
||||
|
||||
$jwk = Utilities\RequestSigner::JWKString(
|
||||
$payload,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getNewAccount(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_account->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getNewAccount(),
|
||||
$jwk
|
||||
);
|
||||
|
||||
return new Response\Account\Get($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class GetData extends AbstractLocation {
|
||||
|
||||
protected function _getPayload() : array {
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Account\GetData
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
return new Response\Account\GetData($this->_getRawResponse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
|
||||
class Update extends AbstractLocation {
|
||||
|
||||
protected $_newEmail;
|
||||
|
||||
public function __construct(Account $account, $newEmail) {
|
||||
|
||||
parent::__construct($account);
|
||||
|
||||
$this->_newEmail = $newEmail;
|
||||
}
|
||||
|
||||
protected function _getPayload() : array {
|
||||
|
||||
return [
|
||||
'contact' => $this->_buildContactPayload($this->_newEmail),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Account\Update
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
return new Response\Account\Update($this->_getRawResponse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Authorization;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Response;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
|
||||
class Get extends AbstractRequest {
|
||||
|
||||
protected $_account;
|
||||
protected $_authorizationURL;
|
||||
|
||||
public function __construct(Account $account, string $authorizationURL) {
|
||||
|
||||
$this->_account = $account;
|
||||
$this->_authorizationURL = $authorizationURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Authorization\Get
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$kid = Utilities\RequestSigner::KID(
|
||||
null,
|
||||
Cache\AccountResponse::getInstance()->get($this->_account)->getLocation(),
|
||||
$this->_authorizationURL,
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_account->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
$this->_authorizationURL,
|
||||
$kid
|
||||
);
|
||||
|
||||
return new Response\Authorization\Get($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Authorization;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Response;
|
||||
use LE_ACME2\Struct\ChallengeAuthorizationKey;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
use LE_ACME2\Account;
|
||||
use LE_ACME2\Order;
|
||||
|
||||
class Start extends AbstractRequest {
|
||||
|
||||
protected $_account;
|
||||
protected $_order;
|
||||
protected $_challenge;
|
||||
|
||||
public function __construct(Account $account, Order $order, Response\Authorization\Struct\Challenge $challenge) {
|
||||
|
||||
$this->_account = $account;
|
||||
$this->_order = $order;
|
||||
$this->_challenge = $challenge;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Authorization\Start
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$payload = [
|
||||
'keyAuthorization' => (new ChallengeAuthorizationKey($this->_account))->get($this->_challenge->token)
|
||||
];
|
||||
|
||||
$kid = Utilities\RequestSigner::KID(
|
||||
$payload,
|
||||
Cache\AccountResponse::getInstance()->get($this->_account)->getLocation(),
|
||||
$this->_challenge->url,
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_account->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
$this->_challenge->url,
|
||||
$kid
|
||||
);
|
||||
|
||||
return new Response\Authorization\Start($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector\Connector;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class GetDirectory extends AbstractRequest {
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\GetDirectory
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$connector = Connector::getInstance();
|
||||
|
||||
$result = $connector->request(
|
||||
Connector::METHOD_GET,
|
||||
$connector->getBaseURL() . '/directory'
|
||||
);
|
||||
return new Response\GetDirectory($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class GetNewNonce extends AbstractRequest {
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\GetNewNonce
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_HEAD,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getNewNonce()
|
||||
);
|
||||
|
||||
return new Response\GetNewNonce($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
use LE_ACME2\Order;
|
||||
|
||||
class Create extends AbstractRequest {
|
||||
|
||||
protected $_order;
|
||||
|
||||
public function __construct(Order $order) {
|
||||
|
||||
$this->_order = $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Order\Create
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$identifiers = [];
|
||||
foreach($this->_order->getSubjects() as $subject) {
|
||||
|
||||
$identifiers[] = [
|
||||
'type' => 'dns',
|
||||
'value' => $subject
|
||||
];
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'identifiers' => $identifiers,
|
||||
'notBefore' => '',
|
||||
'notAfter' => '',
|
||||
];
|
||||
|
||||
$kid = Utilities\RequestSigner::KID(
|
||||
$payload,
|
||||
Cache\AccountResponse::getInstance()->get($this->_order->getAccount())->getLocation(),
|
||||
Cache\DirectoryResponse::getInstance()->get()->getNewOrder(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_order->getAccount()->getKeyDirectoryPath()
|
||||
);
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getNewOrder(),
|
||||
$kid
|
||||
);
|
||||
|
||||
return new Response\Order\Create($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
use LE_ACME2\Order;
|
||||
|
||||
class Finalize extends AbstractRequest {
|
||||
|
||||
protected $_order;
|
||||
protected $_orderResponse;
|
||||
|
||||
public function __construct(Order $order, Response\Order\AbstractOrder $orderResponse) {
|
||||
|
||||
$this->_order = $order;
|
||||
$this->_orderResponse = $orderResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Order\Finalize
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$csr = Utilities\Certificate::generateCSR($this->_order);
|
||||
|
||||
if(preg_match('~-----BEGIN\sCERTIFICATE\sREQUEST-----(.*)-----END\sCERTIFICATE\sREQUEST-----~s', $csr, $matches))
|
||||
$csr = $matches[1];
|
||||
|
||||
$csr = trim(Utilities\Base64::UrlSafeEncode(base64_decode($csr)));
|
||||
|
||||
$payload = [
|
||||
'csr' => $csr
|
||||
];
|
||||
|
||||
$kid = Utilities\RequestSigner::KID(
|
||||
$payload,
|
||||
Cache\AccountResponse::getInstance()->get($this->_order->getAccount())->getLocation(),
|
||||
$this->_orderResponse->getFinalize(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_order->getAccount()->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
$this->_orderResponse->getFinalize(),
|
||||
$kid
|
||||
);
|
||||
|
||||
return new Response\Order\Finalize($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
use LE_ACME2\Order;
|
||||
|
||||
class Get extends AbstractRequest {
|
||||
|
||||
protected $_order;
|
||||
protected $_orderResponse;
|
||||
|
||||
public function __construct(Order $order, Response\Order\AbstractOrder $orderResponse) {
|
||||
|
||||
$this->_order = $order;
|
||||
$this->_orderResponse = $orderResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Order\Get
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$kid = Utilities\RequestSigner::KID(
|
||||
null,
|
||||
Cache\AccountResponse::getInstance()->get($this->_order->getAccount())->getLocation(),
|
||||
$this->_orderResponse->getLocation(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_order->getAccount()->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
$this->_orderResponse->getLocation(),
|
||||
$kid
|
||||
);
|
||||
|
||||
return new Response\Order\Get($result, $this->_orderResponse->getLocation());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Order;
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
use LE_ACME2\Response;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
class GetCertificate extends AbstractRequest {
|
||||
|
||||
protected $_order;
|
||||
protected $_orderResponse;
|
||||
|
||||
private $_alternativeUrl = null;
|
||||
|
||||
public function __construct(Order $order, Response\Order\AbstractOrder $orderResponse,
|
||||
string $alternativeUrl = null
|
||||
) {
|
||||
$this->_order = $order;
|
||||
$this->_orderResponse = $orderResponse;
|
||||
|
||||
if($alternativeUrl !== null) {
|
||||
$this->_alternativeUrl = $alternativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Order\GetCertificate
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$url = $this->_alternativeUrl === null ?
|
||||
$this->_orderResponse->getCertificate() :
|
||||
$this->_alternativeUrl;
|
||||
|
||||
$kid = Utilities\RequestSigner::KID(
|
||||
null,
|
||||
Cache\AccountResponse::getInstance()->get($this->_order->getAccount())->getLocation(),
|
||||
$url,
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_order->getAccount()->getKeyDirectoryPath()
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
$url,
|
||||
$kid
|
||||
);
|
||||
|
||||
return new Response\Order\GetCertificate($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Request\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response;
|
||||
use LE_ACME2\Request\AbstractRequest;
|
||||
|
||||
use LE_ACME2\Connector;
|
||||
use LE_ACME2\Cache;
|
||||
use LE_ACME2\Exception;
|
||||
use LE_ACME2\Struct;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
class RevokeCertificate extends AbstractRequest {
|
||||
|
||||
protected $_certificateBundle;
|
||||
protected $_reason;
|
||||
|
||||
public function __construct(Struct\CertificateBundle $certificateBundle, $reason) {
|
||||
|
||||
$this->_certificateBundle = $certificateBundle;
|
||||
$this->_reason = $reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response\AbstractResponse|Response\Order\RevokeCertificate
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function getResponse() : Response\AbstractResponse {
|
||||
|
||||
$certificate = file_get_contents($this->_certificateBundle->path . $this->_certificateBundle->certificate);
|
||||
preg_match('~-----BEGIN\sCERTIFICATE-----(.*)-----END\sCERTIFICATE-----~s', $certificate, $matches);
|
||||
$certificate = trim(Utilities\Base64::UrlSafeEncode(base64_decode(trim($matches[1]))));
|
||||
|
||||
$payload = [
|
||||
'certificate' => $certificate,
|
||||
'reason' => $this->_reason
|
||||
];
|
||||
|
||||
$jwk = Utilities\RequestSigner::JWKString(
|
||||
$payload,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getRevokeCert(),
|
||||
Cache\NewNonceResponse::getInstance()->get()->getNonce(),
|
||||
$this->_certificateBundle->path,
|
||||
$this->_certificateBundle->private
|
||||
);
|
||||
|
||||
$result = Connector\Connector::getInstance()->request(
|
||||
Connector\Connector::METHOD_POST,
|
||||
Cache\DirectoryResponse::getInstance()->get()->getRevokeCert(),
|
||||
$jwk
|
||||
);
|
||||
|
||||
return new Response\Order\RevokeCertificate($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
use LE_ACME2\Connector\RawResponse;
|
||||
|
||||
abstract class AbstractResponse {
|
||||
|
||||
protected $_raw = NULL;
|
||||
|
||||
protected $_pattern_header_location = '/^Location: (\S+)$/i';
|
||||
|
||||
/**
|
||||
* AbstractResponse constructor.
|
||||
*
|
||||
* @param RawResponse $raw
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function __construct(RawResponse $raw) {
|
||||
|
||||
$this->_raw = $raw;
|
||||
|
||||
if($this->_isRateLimitReached()) {
|
||||
|
||||
$detail = "";
|
||||
if(isset($raw->body['type']) && $raw->body['type'] == 'urn:ietf:params:acme:error:rateLimited') {
|
||||
$detail = $raw->body['detail'];
|
||||
}
|
||||
|
||||
throw new Exception\RateLimitReached($raw->request, $detail);
|
||||
}
|
||||
|
||||
$result = $this->_isValid();
|
||||
if(!$result) {
|
||||
|
||||
$responseStatus = $this->_preg_match_headerLine('/^HTTP\/.* [0-9]{3,} /i');
|
||||
throw new Exception\InvalidResponse(
|
||||
$raw,
|
||||
$responseStatus ? $responseStatus[1] : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function _preg_match_headerLine(string $pattern) : ?array {
|
||||
|
||||
foreach($this->_raw->header as $line) {
|
||||
|
||||
if(preg_match($pattern, $line, $matches) === 1)
|
||||
return $matches;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function _isRateLimitReached() : bool {
|
||||
return $this->_preg_match_headerLine('/^HTTP\/.* 429/i') !== null;
|
||||
}
|
||||
|
||||
protected function _isValid() : bool {
|
||||
|
||||
return $this->_preg_match_headerLine('/^HTTP\/.* 201/i') !== null || //Created
|
||||
$this->_preg_match_headerLine('/^HTTP\/.* 200/i') !== null ||
|
||||
$this->_preg_match_headerLine('/^HTTP\/.* 204/i') !== null;
|
||||
}
|
||||
|
||||
public function getRaw() : RawResponse {
|
||||
return $this->_raw;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
|
||||
abstract class AbstractAccount extends AbstractResponse {
|
||||
|
||||
const STATUS_VALID = 'valid';
|
||||
|
||||
|
||||
public function getLocation() : string {
|
||||
|
||||
$matches = $this->_preg_match_headerLine($this->_pattern_header_location);
|
||||
return trim($matches[1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
|
||||
abstract class AbstractLocation extends AbstractResponse {
|
||||
|
||||
public function getKey() : string {
|
||||
return $this->_raw->body['key'];
|
||||
}
|
||||
|
||||
public function getContact() : string {
|
||||
return $this->_raw->body['contact'];
|
||||
}
|
||||
|
||||
public function getAgreement() : string {
|
||||
return $this->_raw->body['agreement'];
|
||||
}
|
||||
|
||||
public function getInitialIP() : string {
|
||||
return $this->_raw->body['initialIp'];
|
||||
}
|
||||
|
||||
public function getCreatedAt() : string {
|
||||
return $this->_raw->body['createdAt'];
|
||||
}
|
||||
|
||||
public function getStatus() : string {
|
||||
return $this->_raw->body['status'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
|
||||
class ChangeKeys extends AbstractResponse {}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Create extends AbstractAccount {}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Deactivate extends AbstractLocation {}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Get extends AbstractAccount {}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class GetData extends AbstractLocation {}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Account;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Update extends AbstractLocation {}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Authorization;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
|
||||
use LE_ACME2\Connector\RawResponse;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class AbstractAuthorization extends AbstractResponse {
|
||||
|
||||
/**
|
||||
* AbstractAuthorization constructor.
|
||||
* @param RawResponse $raw
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
public function __construct(RawResponse $raw) {
|
||||
parent::__construct($raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception\ExpiredAuthorization
|
||||
*/
|
||||
protected function _isValid() : bool {
|
||||
|
||||
if($this->_preg_match_headerLine('/^HTTP\/.* 404/i') !== null) {
|
||||
throw new Exception\ExpiredAuthorization();
|
||||
}
|
||||
|
||||
return parent::_isValid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Authorization;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\Authorization\Struct;
|
||||
|
||||
class Get extends AbstractAuthorization {
|
||||
|
||||
public function getIdentifier() : Struct\Identifier {
|
||||
return new Struct\Identifier($this->_raw->body['identifier']['type'], $this->_raw->body['identifier']['value']);
|
||||
}
|
||||
|
||||
public function getStatus() : string {
|
||||
return $this->_raw->body['status'];
|
||||
}
|
||||
|
||||
public function getExpires() : string {
|
||||
return $this->_raw->body['expires'];
|
||||
}
|
||||
|
||||
public function getChallenges() : array {
|
||||
return $this->_raw->body['challenges'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @return Struct\Challenge
|
||||
*/
|
||||
public function getChallenge(string $type) : Struct\Challenge {
|
||||
|
||||
foreach($this->getChallenges() as $challenge) {
|
||||
|
||||
if($type == $challenge['type'])
|
||||
return new Struct\Challenge($challenge['type'], $challenge['status'], $challenge['url'], $challenge['token']);
|
||||
}
|
||||
throw new \RuntimeException('No challenge found with given type');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Authorization;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Start extends AbstractAuthorization {}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Authorization\Struct;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Challenge {
|
||||
|
||||
const STATUS_PROGRESSING = 'processing';
|
||||
const STATUS_PENDING = 'pending';
|
||||
const STATUS_VALID = 'valid';
|
||||
const STATUS_INVALID = 'invalid';
|
||||
|
||||
public $type;
|
||||
public $status;
|
||||
public $url;
|
||||
public $token;
|
||||
|
||||
public function __construct(string $type, string $status, string $url, string $token) {
|
||||
|
||||
$this->type = $type;
|
||||
$this->status = $status;
|
||||
$this->url = $url;
|
||||
$this->token = $token;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Authorization\Struct;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Identifier {
|
||||
|
||||
public $type;
|
||||
public $value;
|
||||
|
||||
public function __construct(string $type, string $value) {
|
||||
|
||||
$this->type = $type;
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class GetDirectory extends AbstractResponse {
|
||||
|
||||
public function getKeyChange() : string {
|
||||
return $this->_raw->body['keyChange'];
|
||||
}
|
||||
|
||||
public function getNewAccount() : string {
|
||||
return $this->_raw->body['newAccount'];
|
||||
}
|
||||
|
||||
public function getNewNonce() : string {
|
||||
return $this->_raw->body['newNonce'];
|
||||
}
|
||||
|
||||
public function getNewOrder() : string {
|
||||
return $this->_raw->body['newOrder'];
|
||||
}
|
||||
|
||||
public function getRevokeCert() : string {
|
||||
return $this->_raw->body['revokeCert'];
|
||||
}
|
||||
|
||||
public function getTermsOfService() : string {
|
||||
return $this->_raw->body['meta']['termsOfService'];
|
||||
}
|
||||
|
||||
public function getWebsite() : string {
|
||||
return $this->_raw->body['meta']['website'];
|
||||
}
|
||||
|
||||
public function getCaaIdentities() : string {
|
||||
return $this->_raw->body['meta']['caaIdentities'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class GetNewNonce extends AbstractResponse {
|
||||
|
||||
protected $_pattern = '/^Replay\-Nonce: (\S+)$/i';
|
||||
|
||||
protected function _isValid() : bool {
|
||||
return $this->_preg_match_headerLine($this->_pattern) !== null;
|
||||
}
|
||||
|
||||
public function getNonce() : string {
|
||||
|
||||
$matches = $this->_preg_match_headerLine($this->_pattern);
|
||||
return trim($matches[1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
abstract class AbstractOrder extends AbstractResponse {
|
||||
|
||||
const STATUS_PENDING = 'pending';
|
||||
const STATUS_VALID = 'valid';
|
||||
const STATUS_READY = 'ready';
|
||||
const STATUS_INVALID = 'invalid';
|
||||
|
||||
public function getLocation() : string {
|
||||
|
||||
$matches = $this->_preg_match_headerLine($this->_pattern_header_location);
|
||||
return trim($matches[1]);
|
||||
}
|
||||
|
||||
public function getStatus() : string {
|
||||
return $this->_raw->body['status'];
|
||||
}
|
||||
|
||||
public function getExpires() : string {
|
||||
return $this->_raw->body['expires'];
|
||||
}
|
||||
|
||||
public function getIdentifiers() : array {
|
||||
return $this->_raw->body['identifiers'];
|
||||
}
|
||||
|
||||
public function getAuthorizations() : array {
|
||||
return $this->_raw->body['authorizations'];
|
||||
}
|
||||
|
||||
public function getFinalize() : string {
|
||||
return $this->_raw->body['finalize'];
|
||||
}
|
||||
|
||||
public function getCertificate() : string {
|
||||
return $this->_raw->body['certificate'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception\StatusInvalid
|
||||
*/
|
||||
protected function _isValid(): bool {
|
||||
|
||||
if(!parent::_isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(
|
||||
$this->getStatus() == AbstractOrder::STATUS_INVALID
|
||||
) {
|
||||
throw new Exception\StatusInvalid('Order has status "' . AbstractOrder::STATUS_INVALID . '"'.
|
||||
'. Probably all authorizations have failed. ' . PHP_EOL .
|
||||
'Please see: ' . $this->getLocation() . PHP_EOL .
|
||||
'Continue by using $order->clear() after getting rid of the problem'
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Create extends AbstractOrder {}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Finalize extends AbstractOrder {}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Connector\RawResponse;
|
||||
use LE_ACME2\Exception;
|
||||
|
||||
class Get extends AbstractOrder {
|
||||
|
||||
/**
|
||||
* Get constructor.
|
||||
*
|
||||
* @param RawResponse $raw
|
||||
* @param $orderURL
|
||||
* @throws Exception\InvalidResponse
|
||||
* @throws Exception\RateLimitReached
|
||||
*/
|
||||
public function __construct(RawResponse $raw, string $orderURL) {
|
||||
|
||||
// Dirty fix: Header of response "Get" does not contain an order url, instead of response "Create"
|
||||
// Is needed on production server, not on staging server - tested: 12.04.2021
|
||||
$raw->header[] = 'Location: ' . $orderURL;
|
||||
|
||||
parent::__construct($raw);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
|
||||
class GetCertificate extends AbstractResponse {
|
||||
|
||||
protected $_pattern = '~(-----BEGIN\sCERTIFICATE-----[\s\S]+?-----END\sCERTIFICATE-----)~i';
|
||||
|
||||
|
||||
public function getCertificate() : string {
|
||||
|
||||
if(preg_match_all($this->_pattern, $this->_raw->body, $matches)) {
|
||||
|
||||
return $matches[0][0];
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Preg_match_all has returned false - invalid pattern?');
|
||||
}
|
||||
|
||||
public function getIntermediate() : string {
|
||||
|
||||
if(preg_match_all($this->_pattern, $this->_raw->body, $matches)) {
|
||||
|
||||
$result = '';
|
||||
|
||||
for($i=1; $i<count($matches[0]); $i++) {
|
||||
|
||||
$result .= "\n" . $matches[0][$i];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Preg_match_all has returned false - invalid pattern?');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAlternativeLinks() : array {
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach($this->_raw->header as $line) {
|
||||
$matches = [];
|
||||
preg_match_all('/^link: <(.*)>;rel="alternate"$/', $line, $matches);
|
||||
|
||||
if(isset($matches[1][0])) {
|
||||
$result[] = $matches[1][0];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Response\Order;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Response\AbstractResponse;
|
||||
|
||||
class RevokeCertificate extends AbstractResponse {}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace LE_ACME2;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
trait SingletonTrait {
|
||||
|
||||
private static $_instance = NULL;
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
final public static function getInstance(): self {
|
||||
|
||||
if( self::$_instance === NULL ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Struct;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class CertificateBundle {
|
||||
|
||||
public $path;
|
||||
public $private;
|
||||
public $certificate;
|
||||
public $intermediate;
|
||||
public $expireTime;
|
||||
|
||||
public function __construct(string $path, string $private, string $certificate, string $intermediate, int $expireTime) {
|
||||
|
||||
$this->path = $path;
|
||||
$this->private = $private;
|
||||
$this->certificate = $certificate;
|
||||
$this->intermediate = $intermediate;
|
||||
$this->expireTime = $expireTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Struct;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Account;
|
||||
use LE_ACME2\Utilities;
|
||||
|
||||
class ChallengeAuthorizationKey {
|
||||
|
||||
private $_account;
|
||||
|
||||
public function __construct(Account $account) {
|
||||
$this->_account = $account;
|
||||
}
|
||||
|
||||
public function get(string $token) : string {
|
||||
return $token . '.' . $this->_getDigest();
|
||||
}
|
||||
|
||||
public function getEncoded(string $token) : string {
|
||||
return Utilities\Base64::UrlSafeEncode(
|
||||
hash('sha256', $this->get($token), true)
|
||||
);
|
||||
}
|
||||
|
||||
private function _getDigest() : string {
|
||||
|
||||
$privateKey = openssl_pkey_get_private(file_get_contents($this->_account->getKeyDirectoryPath() . 'private.pem'));
|
||||
$details = openssl_pkey_get_details($privateKey);
|
||||
|
||||
$header = array(
|
||||
"e" => Utilities\Base64::UrlSafeEncode($details["rsa"]["e"]),
|
||||
"kty" => "RSA",
|
||||
"n" => Utilities\Base64::UrlSafeEncode($details["rsa"]["n"])
|
||||
|
||||
);
|
||||
return Utilities\Base64::UrlSafeEncode(hash('sha256', json_encode($header), true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Utilities;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class Base64 {
|
||||
|
||||
/**
|
||||
* Encodes a string input to a base64 encoded string which is URL safe.
|
||||
*
|
||||
* @param string $input The input string to encode.
|
||||
* @return string Returns a URL safe base64 encoded string.
|
||||
*/
|
||||
public static function UrlSafeEncode(string $input) : string {
|
||||
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string that is URL safe base64 encoded.
|
||||
*
|
||||
* @param string $input The encoded input string to decode.
|
||||
* @return string Returns the decoded input string.
|
||||
*/
|
||||
public static function UrlSafeDecode(string $input) : string {
|
||||
|
||||
$remainder = strlen($input) % 4;
|
||||
if ($remainder) {
|
||||
$padlen = 4 - $remainder;
|
||||
$input .= str_repeat('=', $padlen);
|
||||
}
|
||||
return base64_decode(strtr($input, '-_', '+/'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Utilities;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Order;
|
||||
use LE_ACME2\Exception\OpenSSLException;
|
||||
|
||||
class Certificate {
|
||||
|
||||
protected static $_featureOCSPMustStapleEnabled = false;
|
||||
|
||||
public static function enableFeatureOCSPMustStaple() {
|
||||
self::$_featureOCSPMustStapleEnabled = true;
|
||||
}
|
||||
|
||||
public static function disableFeatureOCSPMustStaple() {
|
||||
self::$_featureOCSPMustStapleEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Order $order
|
||||
* @return string
|
||||
* @throws OpenSSLException
|
||||
*/
|
||||
public static function generateCSR(Order $order) : string {
|
||||
|
||||
$dn = [
|
||||
"commonName" => $order->getSubjects()[0]
|
||||
];
|
||||
|
||||
$san = implode(",", array_map(function ($dns) {
|
||||
|
||||
return "DNS:" . $dns;
|
||||
}, $order->getSubjects())
|
||||
);
|
||||
|
||||
$configFilePath = $order->getKeyDirectoryPath() . 'csr_config';
|
||||
|
||||
$config = 'HOME = .
|
||||
RANDFILE = ' . $order->getKeyDirectoryPath() . '.rnd
|
||||
[ req ]
|
||||
default_bits = 4096
|
||||
default_keyfile = privkey.pem
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
[ req_distinguished_name ]
|
||||
countryName = Country Name (2 letter code)
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
subjectAltName = ' . $san . '
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment';
|
||||
|
||||
if(self::$_featureOCSPMustStapleEnabled) {
|
||||
$config .= PHP_EOL . 'tlsfeature=status_request';
|
||||
}
|
||||
|
||||
file_put_contents($configFilePath, $config);
|
||||
|
||||
$privateKey = openssl_pkey_get_private(
|
||||
file_get_contents($order->getKeyDirectoryPath() . 'private.pem')
|
||||
);
|
||||
|
||||
if($privateKey === false) {
|
||||
throw new OpenSSLException('openssl_pkey_get_private');
|
||||
}
|
||||
|
||||
$csr = openssl_csr_new(
|
||||
$dn,
|
||||
$privateKey,
|
||||
[
|
||||
'config' => $configFilePath,
|
||||
'digest_alg' => 'sha256'
|
||||
]
|
||||
);
|
||||
|
||||
if($csr === false) {
|
||||
throw new OpenSSLException('openssl_csr_new');
|
||||
}
|
||||
|
||||
if(!openssl_csr_export($csr, $csr)) {
|
||||
throw new OpenSSLException('openssl_csr_export');
|
||||
}
|
||||
|
||||
unlink($configFilePath);
|
||||
|
||||
return $csr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Utilities;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class KeyGenerator {
|
||||
|
||||
/**
|
||||
* Generates a new RSA keypair and saves both keys to a new file.
|
||||
*
|
||||
* @param string $directory The directory in which to store the new keys.
|
||||
* @param string $privateKeyFile The filename for the private key file.
|
||||
* @param string $publicKeyFile The filename for the public key file.
|
||||
*/
|
||||
public static function RSA(string $directory, string $privateKeyFile = 'private.pem', string $publicKeyFile = 'public.pem') {
|
||||
|
||||
$res = openssl_pkey_new([
|
||||
"private_key_type" => OPENSSL_KEYTYPE_RSA,
|
||||
"private_key_bits" => 4096,
|
||||
]);
|
||||
|
||||
if(!openssl_pkey_export($res, $privateKey))
|
||||
throw new \RuntimeException("RSA keypair export failed!");
|
||||
|
||||
$details = openssl_pkey_get_details($res);
|
||||
|
||||
file_put_contents($directory . $privateKeyFile, $privateKey);
|
||||
file_put_contents($directory . $publicKeyFile, $details['key']);
|
||||
|
||||
if(PHP_MAJOR_VERSION < 8) {
|
||||
// deprecated after PHP 8.0.0 and not needed anymore
|
||||
openssl_pkey_free($res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new EC prime256v1 keypair and saves both keys to a new file.
|
||||
*
|
||||
* @param string $directory The directory in which to store the new keys.
|
||||
* @param string $privateKeyFile The filename for the private key file.
|
||||
* @param string $publicKeyFile The filename for the public key file.
|
||||
*/
|
||||
public static function EC(string $directory, string $privateKeyFile = 'private.pem', string $publicKeyFile = 'public.pem') {
|
||||
|
||||
if (version_compare(PHP_VERSION, '7.1.0') == -1)
|
||||
throw new \RuntimeException("PHP 7.1+ required for EC keys");
|
||||
|
||||
$res = openssl_pkey_new([
|
||||
"private_key_type" => OPENSSL_KEYTYPE_EC,
|
||||
"curve_name" => "prime256v1",
|
||||
]);
|
||||
|
||||
if(!openssl_pkey_export($res, $privateKey))
|
||||
throw new \RuntimeException("EC keypair export failed!");
|
||||
|
||||
$details = openssl_pkey_get_details($res);
|
||||
|
||||
file_put_contents($directory . $privateKeyFile, $privateKey);
|
||||
file_put_contents($directory . $publicKeyFile, $details['key']);
|
||||
|
||||
if(PHP_MAJOR_VERSION < 8) {
|
||||
// deprecated after PHP 8.0.0 and not needed anymore
|
||||
openssl_pkey_free($res);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Utilities;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\SingletonTrait;
|
||||
|
||||
class Logger {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
const LEVEL_DISABLED = 0;
|
||||
const LEVEL_INFO = 1;
|
||||
const LEVEL_DEBUG = 2;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
protected $_desiredLevel = self::LEVEL_DISABLED;
|
||||
|
||||
public function setDesiredLevel(int $desiredLevel) {
|
||||
$this->_desiredLevel = $desiredLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
* @param string $message
|
||||
* @param string|array|object $data
|
||||
*/
|
||||
public function add(int $level, string $message, $data = array()) {
|
||||
|
||||
if($level > $this->_desiredLevel)
|
||||
return;
|
||||
|
||||
$e = new \Exception();
|
||||
$trace = $e->getTrace();
|
||||
unset($trace[0]);
|
||||
|
||||
$output = '<b>' . date('d-m-Y H:i:s') . ': ' . $message . '</b><br>' . "\n";
|
||||
|
||||
if($this->_desiredLevel == self::LEVEL_DEBUG) {
|
||||
|
||||
$step = 0;
|
||||
foreach ($trace as $traceItem) {
|
||||
|
||||
if(!isset($traceItem['class']) || !isset($traceItem['function'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output .= 'Trace #' . $step . ': ' . $traceItem['class'] . '::' . $traceItem['function'] . '<br/>' . "\n";
|
||||
$step++;
|
||||
}
|
||||
|
||||
if ((is_array($data) && count($data) > 0) || !is_array($data))
|
||||
$output .= "\n" .'<br/>Data:<br/>' . "\n" . '<pre>' . var_export($data, true) . '</pre>';
|
||||
|
||||
$output .= '<br><br>' . "\n\n";
|
||||
}
|
||||
|
||||
if(PHP_SAPI == 'cli') {
|
||||
|
||||
$output = strip_tags($output);
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace LE_ACME2\Utilities;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
class RequestSigner {
|
||||
|
||||
/**
|
||||
* Generates a JSON Web Key signature to attach to the request.
|
||||
*
|
||||
* @param array $payload The payload to add to the signature.
|
||||
* @param string $url The URL to use in the signature.
|
||||
* @param string $nonce
|
||||
* @param string $privateKeyDir The directory to get the private key from. Default to the account keys directory given in the constructor. (optional)
|
||||
* @param string $privateKeyFile The private key to sign the request with. Defaults to 'private.pem'. (optional)
|
||||
*
|
||||
* @return array Returns an array containing the signature.
|
||||
*/
|
||||
public static function JWK(array $payload, string $url, string $nonce, string $privateKeyDir, string $privateKeyFile = 'private.pem') : array {
|
||||
|
||||
Logger::getInstance()->add(Logger::LEVEL_DEBUG, 'JWK sign request for ' . $url, $payload);
|
||||
|
||||
$privateKey = openssl_pkey_get_private(file_get_contents($privateKeyDir . $privateKeyFile));
|
||||
$details = openssl_pkey_get_details($privateKey);
|
||||
|
||||
$protected = [
|
||||
"alg" => "RS256",
|
||||
"jwk" => [
|
||||
"kty" => "RSA",
|
||||
"n" => Base64::UrlSafeEncode($details["rsa"]["n"]),
|
||||
"e" => Base64::UrlSafeEncode($details["rsa"]["e"]),
|
||||
],
|
||||
"nonce" => $nonce,
|
||||
"url" => $url
|
||||
];
|
||||
|
||||
$payload64 = Base64::UrlSafeEncode(str_replace('\\/', '/', json_encode($payload)));
|
||||
$protected64 = Base64::UrlSafeEncode(json_encode($protected));
|
||||
|
||||
openssl_sign($protected64.'.'.$payload64, $signed, $privateKey, "SHA256");
|
||||
$signed64 = Base64::UrlSafeEncode($signed);
|
||||
|
||||
$data = array(
|
||||
'protected' => $protected64,
|
||||
'payload' => $payload64,
|
||||
'signature' => $signed64
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a JSON Web Key signature to attach to the request.
|
||||
*
|
||||
* @param array $payload The payload to add to the signature.
|
||||
* @param string $url The URL to use in the signature.
|
||||
* @param string $nonce
|
||||
* @param string $privateKeyDir The directory to get the private key from. Default to the account keys directory given in the constructor. (optional)
|
||||
* @param string $privateKeyFile The private key to sign the request with. Defaults to 'private.pem'. (optional)
|
||||
*
|
||||
* @return string Returns a JSON encoded string containing the signature.
|
||||
*/
|
||||
public static function JWKString(array $payload, string $url, string $nonce, string $privateKeyDir, string $privateKeyFile = 'private.pem') : string {
|
||||
|
||||
$jwk = self::JWK($payload, $url, $nonce, $privateKeyDir, $privateKeyFile);
|
||||
return json_encode($jwk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Key ID signature to attach to the request.
|
||||
*
|
||||
* @param array|null $payload The payload to add to the signature.
|
||||
* @param string $kid The Key ID to use in the signature.
|
||||
* @param string $url The URL to use in the signature.
|
||||
* @param string $nonce
|
||||
* @param string $privateKeyDir The directory to get the private key from.
|
||||
* @param string $privateKeyFile The private key to sign the request with. Defaults to 'private.pem'. (optional)
|
||||
*
|
||||
* @return string Returns a JSON encoded string containing the signature.
|
||||
*/
|
||||
public static function KID(?array $payload, string $kid, string $url, string $nonce, string $privateKeyDir, string $privateKeyFile = 'private.pem') : string {
|
||||
|
||||
Logger::getInstance()->add(Logger::LEVEL_DEBUG, 'KID sign request for ' . $url, $payload);
|
||||
|
||||
$privateKey = openssl_pkey_get_private(file_get_contents($privateKeyDir . $privateKeyFile));
|
||||
// TODO: unused - $details = openssl_pkey_get_details($privateKey);
|
||||
|
||||
$protected = [
|
||||
"alg" => "RS256",
|
||||
"kid" => $kid,
|
||||
"nonce" => $nonce,
|
||||
"url" => $url
|
||||
];
|
||||
|
||||
Logger::getInstance()->add(Logger::LEVEL_DEBUG, 'KID: ready to sign request for: ' . $url, $protected);
|
||||
|
||||
$payload = $payload === null ? "" : str_replace('\\/', '/', json_encode($payload));
|
||||
|
||||
$payload64 = Base64::UrlSafeEncode($payload);
|
||||
$protected64 = Base64::UrlSafeEncode(json_encode($protected));
|
||||
|
||||
openssl_sign($protected64.'.'.$payload64, $signed, $privateKey, "SHA256");
|
||||
$signed64 = Base64::UrlSafeEncode($signed);
|
||||
|
||||
$data = [
|
||||
'protected' => $protected64,
|
||||
'payload' => $payload64,
|
||||
'signature' => $signed64
|
||||
];
|
||||
|
||||
return json_encode($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace LE_ACME2Tests;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use LE_ACME2;
|
||||
|
||||
abstract class AbstractTest extends TestCase {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
LE_ACME2\Connector\Connector::getInstance()->useStagingServer(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
namespace LE_ACME2Tests;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\Exception\InvalidResponse;
|
||||
|
||||
/**
|
||||
* @covers \LE_ACME2\Account
|
||||
*/
|
||||
class AccountTest extends AbstractTest {
|
||||
|
||||
private $_commonKeyDirectoryPath;
|
||||
|
||||
private $_email;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->_commonKeyDirectoryPath = TestHelper::getInstance()->getTempPath() . 'le-storage/';
|
||||
|
||||
$this->_email = 'le_acme2_php_client@test.com';
|
||||
}
|
||||
|
||||
public function testNonExistingCommonKeyDirectoryPath() {
|
||||
|
||||
$this->assertTrue(\LE_ACME2\Account::getCommonKeyDirectoryPath() === null);
|
||||
|
||||
$notExistingPath = TestHelper::getInstance()->getTempPath() . 'should-not-exist/';
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
\LE_ACME2\Account::setCommonKeyDirectoryPath($notExistingPath);
|
||||
}
|
||||
|
||||
public function testCommonKeyDirectoryPath() {
|
||||
|
||||
if(!file_exists($this->_commonKeyDirectoryPath)) {
|
||||
mkdir($this->_commonKeyDirectoryPath);
|
||||
}
|
||||
|
||||
\LE_ACME2\Account::setCommonKeyDirectoryPath($this->_commonKeyDirectoryPath);
|
||||
|
||||
$this->assertTrue(
|
||||
\LE_ACME2\Account::getCommonKeyDirectoryPath() === $this->_commonKeyDirectoryPath
|
||||
);
|
||||
}
|
||||
|
||||
public function testNonExisting() {
|
||||
|
||||
if(\LE_ACME2\Account::exists($this->_email)) {
|
||||
$this->markTestSkipped('Skipped: Account does already exist');
|
||||
}
|
||||
|
||||
$this->assertTrue(!\LE_ACME2\Account::exists($this->_email));
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
\LE_ACME2\Account::get($this->_email);
|
||||
}
|
||||
|
||||
public function testCreate() {
|
||||
|
||||
if(\LE_ACME2\Account::exists($this->_email)) {
|
||||
// Skipping account modification tests, when the account already exists
|
||||
// to reduce the LE api usage while developing
|
||||
TestHelper::getInstance()->setSkipAccountModificationTests(true);
|
||||
$this->markTestSkipped('Account modifications skipped: Account does already exist');
|
||||
}
|
||||
|
||||
$this->assertTrue(!\LE_ACME2\Account::exists($this->_email));
|
||||
|
||||
$account = \LE_ACME2\Account::create($this->_email);
|
||||
$this->assertTrue(is_object($account));
|
||||
$this->assertTrue($account->getEmail() === $this->_email);
|
||||
|
||||
$account = \LE_ACME2\Account::get($this->_email);
|
||||
$this->assertTrue(is_object($account));
|
||||
|
||||
$result = $account->getData();
|
||||
$this->assertTrue($result->getStatus() === \LE_ACME2\Response\Account\AbstractAccount::STATUS_VALID);
|
||||
}
|
||||
|
||||
public function testInvalidCreate() {
|
||||
|
||||
if(TestHelper::getInstance()->shouldSkipAccountModificationTests()) {
|
||||
$this->expectNotToPerformAssertions();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->expectException(InvalidResponse::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Invalid response received: ' .
|
||||
'urn:ietf:params:acme:error:invalidEmail' .
|
||||
' - ' .
|
||||
'Error creating new account :: invalid contact domain. Contact emails @example.org are forbidden'
|
||||
);
|
||||
\LE_ACME2\Account::create('test@example.org');
|
||||
}
|
||||
|
||||
public function testModification() {
|
||||
|
||||
if(TestHelper::getInstance()->shouldSkipAccountModificationTests()) {
|
||||
$this->expectNotToPerformAssertions();
|
||||
return;
|
||||
}
|
||||
|
||||
$account = \LE_ACME2\Account::get($this->_email);
|
||||
$this->assertTrue(is_object($account));
|
||||
|
||||
$keyDirectoryPath = $account->getKeyDirectoryPath();
|
||||
$newEmail = 'new-' . $this->_email;
|
||||
|
||||
// An email from example.org is not allowed
|
||||
$result = $account->update('test@example.org');
|
||||
$this->assertTrue($result === false);
|
||||
|
||||
$result = $account->update($newEmail);
|
||||
$this->assertTrue($result === true);
|
||||
|
||||
$this->assertTrue($account->getKeyDirectoryPath() !== $keyDirectoryPath);
|
||||
$this->assertTrue(file_exists($account->getKeyDirectoryPath()));
|
||||
|
||||
$result = $account->update($this->_email);
|
||||
$this->assertTrue($result === true);
|
||||
|
||||
$result = $account->changeKeys();
|
||||
$this->assertTrue($result === true);
|
||||
}
|
||||
|
||||
public function testDeactivation() {
|
||||
|
||||
if(TestHelper::getInstance()->shouldSkipAccountModificationTests()) {
|
||||
$this->expectNotToPerformAssertions();
|
||||
return;
|
||||
}
|
||||
|
||||
$account = \LE_ACME2\Account::get($this->_email);
|
||||
$this->assertTrue(is_object($account));
|
||||
|
||||
$result = $account->deactivate();
|
||||
$this->assertTrue($result === true);
|
||||
|
||||
// The account is already deactivated
|
||||
$result = $account->deactivate();
|
||||
$this->assertTrue($result === false);
|
||||
|
||||
// The account is already deactivated
|
||||
$result = $account->changeKeys();
|
||||
$this->assertTrue($result === false);
|
||||
|
||||
// The account is already deactivated
|
||||
$this->expectException(\LE_ACME2\Exception\InvalidResponse::class);
|
||||
$account->getData();
|
||||
}
|
||||
|
||||
public function testCreationAfterDeactivation() {
|
||||
|
||||
if(TestHelper::getInstance()->shouldSkipAccountModificationTests()) {
|
||||
$this->expectNotToPerformAssertions();
|
||||
return;
|
||||
}
|
||||
|
||||
$account = \LE_ACME2\Account::get($this->_email);
|
||||
$this->assertTrue(is_object($account));
|
||||
|
||||
system('rm -R ' . $account->getKeyDirectoryPath());
|
||||
$this->assertTrue(!\LE_ACME2\Account::exists($this->_email));
|
||||
|
||||
$account = \LE_ACME2\Account::create($this->_email);
|
||||
$this->assertTrue(is_object($account));
|
||||
}
|
||||
|
||||
public function test() {
|
||||
|
||||
$account = \LE_ACME2\Account::get($this->_email);
|
||||
$this->assertTrue(is_object($account));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace LE_ACME2Tests\Authorizer;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2Tests\AbstractTest;
|
||||
use LE_ACME2Tests\TestHelper;
|
||||
|
||||
/**
|
||||
* @covers \LE_ACME2\Authorizer\HTTP
|
||||
*/
|
||||
class HTTPTest extends AbstractTest {
|
||||
|
||||
private $_directoryPath;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->_directoryPath = TestHelper::getInstance()->getTempPath() . 'acme-challenges/';
|
||||
}
|
||||
|
||||
public function testNonExistingDirectoryPath() {
|
||||
|
||||
$this->assertTrue(\LE_ACME2\Authorizer\HTTP::getDirectoryPath() === null);
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
\LE_ACME2\Authorizer\HTTP::setDirectoryPath(TestHelper::getInstance()->getNonExistingPath());
|
||||
}
|
||||
|
||||
public function testDirectoryPath() {
|
||||
|
||||
if(!file_exists($this->_directoryPath)) {
|
||||
mkdir($this->_directoryPath);
|
||||
}
|
||||
|
||||
\LE_ACME2\Authorizer\HTTP::setDirectoryPath($this->_directoryPath);
|
||||
|
||||
$this->assertTrue(
|
||||
\LE_ACME2\Authorizer\HTTP::getDirectoryPath() === $this->_directoryPath
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace LE_ACME2Tests;
|
||||
defined('ABSPATH') or die();
|
||||
|
||||
use LE_ACME2\SingletonTrait;
|
||||
|
||||
class TestHelper {
|
||||
|
||||
private $_tempPath;
|
||||
private $_nonExistingPath;
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
private function __construct() {
|
||||
|
||||
$projectPath = realpath($_SERVER[ 'PWD' ]) . '/';
|
||||
$this->_tempPath = $projectPath . 'temp/';
|
||||
if( !file_exists($this->_tempPath) ) {
|
||||
mkdir($this->_tempPath);
|
||||
}
|
||||
|
||||
$this->_nonExistingPath = $this->getTempPath() . 'should-not-exist/';
|
||||
}
|
||||
|
||||
public function getTempPath() : string {
|
||||
return $this->_tempPath;
|
||||
}
|
||||
|
||||
public function getNonExistingPath() : string {
|
||||
return $this->_nonExistingPath;
|
||||
}
|
||||
|
||||
private $_skipAccountModificationTests = false;
|
||||
|
||||
public function setSkipAccountModificationTests(bool $value) : void {
|
||||
$this->_skipAccountModificationTests = $value;
|
||||
}
|
||||
|
||||
public function shouldSkipAccountModificationTests() : bool {
|
||||
return $this->_skipAccountModificationTests;
|
||||
}
|
||||
}
|
||||
1
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/index.php
vendored
Normal file
1
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/index.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<?php // You don't belong here. ?>
|
||||
6
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/.styleci.yml
vendored
Normal file
6
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/.styleci.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
preset: recommended
|
||||
|
||||
disabled:
|
||||
- align_double_arrow
|
||||
- phpdoc_align
|
||||
- blank_line_after_opening_tag
|
||||
4
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/.travis.yml
vendored
Normal file
4
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/.travis.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
services: docker
|
||||
|
||||
script:
|
||||
- docker-compose run tests
|
||||
6
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/Dockerfile
vendored
Normal file
6
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM php:7.3-cli
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y unzip \
|
||||
&& docker-php-ext-install pcntl \
|
||||
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||
13
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/LICENSE
vendored
Normal file
13
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/LICENSE
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright 1999-2020. Plesk International GmbH.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
66
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/README.md
vendored
Normal file
66
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/README.md
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
## PHP library for Plesk XML-RPC API
|
||||
|
||||
[](https://travis-ci.com/plesk/api-php-lib) [](https://scrutinizer-ci.com/g/plesk/api-php-lib/?branch=master)
|
||||
[](https://styleci.io/repos/26514840)
|
||||
|
||||
PHP object-oriented library for Plesk XML-RPC API.
|
||||
|
||||
## Install Via Composer
|
||||
|
||||
[Composer](https://getcomposer.org/) is a preferable way to install the library:
|
||||
|
||||
`composer require plesk/api-php-lib`
|
||||
|
||||
## Usage Examples
|
||||
|
||||
Here is an example on how to use the library and create a customer with desired properties:
|
||||
```php
|
||||
$client = new \PleskX\Api\Client($host);
|
||||
$client->setCredentials($login, $password);
|
||||
|
||||
$client->customer()->create([
|
||||
'cname' => 'Plesk',
|
||||
'pname' => 'John Smith',
|
||||
'login' => 'john',
|
||||
'passwd' => 'secret',
|
||||
'email' => 'john@smith.com',
|
||||
]);
|
||||
```
|
||||
|
||||
It is possible to use a secret key instead of password for authentication.
|
||||
|
||||
```php
|
||||
$client = new \PleskX\Api\Client($host);
|
||||
$client->setSecretKey($secretKey)
|
||||
```
|
||||
|
||||
In case of Plesk extension creation one can use an internal mechanism to access XML-RPC API. It does not require to pass authentication because the extension works in the context of Plesk.
|
||||
|
||||
```php
|
||||
$client = new \PleskX\Api\InternalClient();
|
||||
$protocols = $client->server()->getProtos();
|
||||
```
|
||||
|
||||
For additional examples see tests/ directory.
|
||||
|
||||
## How to Run Unit Tests
|
||||
|
||||
One the possible ways to become familiar with the library is to check the unit tests.
|
||||
|
||||
To run the unit tests use the following command:
|
||||
|
||||
`REMOTE_HOST=your-plesk-host.dom REMOTE_PASSWORD=password composer test`
|
||||
|
||||
To use custom port one can provide a URL (e.g. for Docker container):
|
||||
|
||||
`REMOTE_URL=https://your-plesk-host.dom:port REMOTE_PASSWORD=password composer test`
|
||||
|
||||
One more way to run tests is to use Docker:
|
||||
|
||||
`docker-compose run tests`
|
||||
|
||||
## Continuous Testing
|
||||
|
||||
During active development it could be more convenient to run tests in continuous manner. Here is the way how to achieve it:
|
||||
|
||||
`REMOTE_URL=https://your-plesk-host.dom:port REMOTE_PASSWORD=password composer test:watch`
|
||||
48
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/composer.json
vendored
Normal file
48
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/composer.json
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "plesk/api-php-lib",
|
||||
"type": "library",
|
||||
"description": "PHP object-oriented library for Plesk XML-RPC API",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alexei Yuzhakov",
|
||||
"email": "sibprogrammer@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Plesk International GmbH.",
|
||||
"email": "plesk-dev-leads@plesk.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3",
|
||||
"ext-curl": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-simplexml": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"spatie/phpunit-watcher": "^1.22"
|
||||
},
|
||||
"config": {
|
||||
"process-timeout": 0
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"test:watch": "phpunit-watcher watch"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PleskX\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PleskXTest\\": "tests/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/docker-compose.yml
vendored
Normal file
21
wp-content/plugins/really-simple-ssl/lets-encrypt/vendor/plesk/api-php-lib/docker-compose.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
version: '2'
|
||||
services:
|
||||
plesk:
|
||||
image: plesk/plesk
|
||||
logging:
|
||||
driver: none
|
||||
ports:
|
||||
["8443:8443"]
|
||||
tests:
|
||||
build: .
|
||||
environment:
|
||||
REMOTE_URL: https://plesk:8443
|
||||
REMOTE_PASSWORD: changeme1Q**
|
||||
command: bash -c "cd /opt/api-php-lib && composer install && ./wait-for-plesk.sh && composer test -- --testdox"
|
||||
depends_on:
|
||||
- plesk
|
||||
links:
|
||||
- plesk
|
||||
volumes:
|
||||
- .:/opt/api-php-lib
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user