From a5026a04122337f3cec270c9a0a9fdba0bead4b5 Mon Sep 17 00:00:00 2001 From: Sebastian Feldmann Date: Sat, 5 Dec 2020 14:17:11 +0100 Subject: [PATCH 1/4] Change license In order to match all other phar-io licenses the license was changed. --- LICENSE | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/LICENSE b/LICENSE index 58bf89c..6c314c0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,30 @@ -Copyright ComposerDistributor-Team +Phar.io - Composer Distributor -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: +Copyright (c) 2020 Arne Blankerts and contributors +All rights reserved. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -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. +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Arne Blankerts nor the names of contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. From 5b1d924d723190b9129eef064a002a7b9daf10a3 Mon Sep 17 00:00:00 2001 From: Sebastian Feldmann Date: Mon, 7 Dec 2020 12:41:06 +0100 Subject: [PATCH 2/4] Add no GnuPG warning If no GnuPG is found a warning is displayed. The install output got colored to match composers internal output. --- src/ConfiguredMediator.php | 41 ++++++++++++++++++-- src/PluginBase.php | 14 +++---- src/Service/Installer.php | 78 +++++++++++++++++++++----------------- 3 files changed, 89 insertions(+), 44 deletions(-) diff --git a/src/ConfiguredMediator.php b/src/ConfiguredMediator.php index e998bfd..b7746cc 100644 --- a/src/ConfiguredMediator.php +++ b/src/ConfiguredMediator.php @@ -7,9 +7,13 @@ use Composer\Composer; use Composer\Installer\PackageEvent; use Composer\IO\IOInterface; +use Exception; +use GnuPG; use PharIo\ComposerDistributor\Config\Config; use PharIo\ComposerDistributor\Config\Loader; use PharIo\ComposerDistributor\Service\Installer; +use PharIo\FileSystem\Directory; +use PharIo\GnuPG\Factory; use SplFileInfo; abstract class ConfiguredMediator extends PluginBase @@ -17,6 +21,9 @@ abstract class ConfiguredMediator extends PluginBase /** @var \PharIo\ComposerDistributor\Config\Config */ private $config; + /** @var bool */ + private static $warningDisplayed = false; + /** * Config has to be loaded on instantiation because on uninstall all external dependencies are * removed before `uninstall` is called and auto-loading any external phar-io dependencies then will fail. @@ -37,15 +44,43 @@ public function uninstall(Composer $composer, IOInterface $io) public function installOrUpdateFunction(PackageEvent $event): void { + $gnuPG = $this->createGnuPG(); + // we do not want to crash if no GnuPG was found + // but display a noticeable warning to the user + if (null === $gnuPG && !self::$warningDisplayed) { + $this->io->write( + PHP_EOL . + ' WARNING' . PHP_EOL . + ' No GPG installation found! Use installed PHARs with care. ' . PHP_EOL . + ' Consider installing GnuPG to verify PHAR authenticity.' . PHP_EOL . + ' If you need help installing GnuPG visit http://phar.io/install-gnupg' . PHP_EOL + ); + self::$warningDisplayed = true; + } + $installer = $this->createInstallerFromConfig($this->config, $event); - $installer->install($this->config->phars()); + $installer->install( + $this->config->phars(), + $this->config->keyDirectory() ? $this->createKeyDirectory($this->config) : null, + $gnuPG + ); + } + + public function createGnuPG(): ?GnuPG + { + $factory = new Factory(); + try { + $gnuPG = $factory->createGnuPG(new Directory(sys_get_temp_dir())); + } catch (Exception $e) { + $gnuPG = null; + } + return $gnuPG; } private function createInstallerFromConfig(Config $config, PackageEvent $event): Installer { return new Installer( $config->package(), - $config->keyDirectory() ? $this->createKeyDirectory($config) : null, $this->io, $event ); @@ -86,7 +121,7 @@ private function deleteFile(File $phar, string $binDir): void ); return; } - $this->io->write(sprintf(' - Removing phar \'%1$s\'', $phar->pharName())); + $this->io->write(sprintf(' - Removing phar %1$s', $phar->pharName())); unlink($pharLocation); } } diff --git a/src/PluginBase.php b/src/PluginBase.php index c5cda4b..65d41a8 100644 --- a/src/PluginBase.php +++ b/src/PluginBase.php @@ -11,30 +11,31 @@ use Composer\IO\IOInterface; use Composer\Plugin\PluginInterface; use PharIo\ComposerDistributor\Service\Installer; -use SplFileInfo; abstract class PluginBase implements PluginInterface, EventSubscriberInterface { + /** @var \Composer\Composer */ protected $composer; + /** @var \Composer\IO\IOInterface */ protected $io; public function activate(Composer $composer, IOInterface $io) { $this->composer = $composer; - $this->io = $io; + $this->io = $io; } public function deactivate(Composer $composer, IOInterface $io) { $this->composer = $composer; - $this->io = $io; + $this->io = $io; } public function uninstall(Composer $composer, IOInterface $io) { $this->composer = $composer; - $this->io = $io; + $this->io = $io; } public static function getSubscribedEvents() @@ -43,17 +44,16 @@ public static function getSubscribedEvents() PackageEvents::POST_PACKAGE_INSTALL => [ ['installOrUpdateFunction', 0], ], - PackageEvents::POST_PACKAGE_UPDATE => [ + PackageEvents::POST_PACKAGE_UPDATE => [ ['installOrUpdateFunction', 0], ], ]; } - public function createInstaller(string $pluginName, string $keyDirectory, PackageEvent $event) : Installer + public function createInstaller(string $pluginName, PackageEvent $event) : Installer { return new Installer( $pluginName, - new KeyDirectory(new SplFileInfo($keyDirectory)), $this->io, $event ); diff --git a/src/Service/Installer.php b/src/Service/Installer.php index 19adfb6..eaaa115 100644 --- a/src/Service/Installer.php +++ b/src/Service/Installer.php @@ -6,8 +6,7 @@ use Composer\Installer\PackageEvent; use Composer\IO\IOInterface; -use PharIo\FileSystem\Directory; -use PharIo\GnuPG\Factory; +use GnuPG; use PharIo\ComposerDistributor\SomebodyElsesProblem; use PharIo\ComposerDistributor\File; use PharIo\ComposerDistributor\FileList; @@ -37,15 +36,17 @@ final class Installer /** @var \Composer\Installer\PackageEvent */ private $event; - public function __construct(string $name, ?KeyDirectory $keys, IOInterface $io, PackageEvent $event) + /** @var \GnuPG|null */ + private $gpg; + + public function __construct(string $name, IOInterface $io, PackageEvent $event) { $this->name = $name; - $this->keys = $keys; $this->io = $io; $this->event = $event; } - public function install(FileList $fileList) : void + public function install(FileList $fileList, ?KeyDirectory $keys, ?GnuPG $gnuPG) : void { try { $packageVersion = PackageVersion::fromPackageEvent($this->event, $this->name); @@ -53,45 +54,57 @@ public function install(FileList $fileList) : void $this->io->write($e->getMessage()); return; } - + $this->keys = $keys; + $this->gpg = $gnuPG; $versionReplacer = new VersionConstraintReplacer($packageVersion); + $binDir = $this->event->getComposer()->getConfig()->get('bin-dir'); + + if (!file_exists($binDir)) { + mkdir($binDir, 0777, true); + } foreach ($fileList->getList() as $file) { $this->io->write(sprintf( - ' - Downloading artifact in version %2$s from %1$s', - $versionReplacer->replace($file->pharUrl()->toString()), - $packageVersion->fullVersion() + ' - Downloading artifact from %1$s', + $versionReplacer->replace($file->pharUrl()->toString()) )); - $pharLocation = $this->downloadPhar($versionReplacer, $file); + $downloadLocation = $this->downloadAndVerify($versionReplacer, $file); + $installLocation = new SplFileInfo($binDir . DIRECTORY_SEPARATOR . $file->pharName()); + + rename($downloadLocation->getPathname(), $installLocation->getPathname()); + chmod($installLocation->getPathname(), 0755); + } + } + + private function downloadAndVerify(VersionConstraintReplacer $versionReplacer, File $file): SplFileInfo + { + $pharLocation = $this->downloadPhar($versionReplacer, $file); - if (!$file->signatureUrl()) { - $this->io->write(' - No digital signature found! Use this file with care!'); - continue; - } + if (!$file->signatureUrl()) { + $this->io->write(' - No digital signature found! Use this file with care!'); + return $pharLocation; + } - $signatureLocation = $this->downloadSignature($versionReplacer, $file); - $this->verifyPharWithSignature($pharLocation, $signatureLocation); - $this->io->write(' - PHAR signature successfully verified'); - unlink($signatureLocation->getPathname()); + if (null === $this->gpg) { + $this->io->write(' - No GnuPG found to verify signature! Use this file with care!'); + return $pharLocation; } + + $signatureLocation = $this->downloadSignature($versionReplacer, $file); + $this->verifyPharWithSignature($pharLocation, $signatureLocation); + $this->io->write(' - PHAR signature successfully verified'); + unlink($signatureLocation->getPathname()); + + return $pharLocation; } private function downloadPhar(VersionConstraintReplacer $versionReplacer, File $file): SplFileInfo { - $binDir = $this->event->getComposer()->getConfig()->get('bin-dir'); - $download = new Download(Url::fromString( - $versionReplacer->replace($file->pharUrl()->toString()) - )); - $pharLocation = new SplFileInfo( - $binDir . DIRECTORY_SEPARATOR . $file->pharName() - ); + $download = new Download(Url::fromString($versionReplacer->replace($file->pharUrl()->toString()))); + $pharLocation = new SplFileInfo(sys_get_temp_dir() . '/' . $file->pharName()); - if (! file_exists($binDir)) { - mkdir($binDir, 0777, true); - } $download->toLocation($pharLocation); - chmod($pharLocation->getRealPath(), 0755); return $pharLocation; } @@ -112,11 +125,8 @@ private function verifyPharWithSignature(SplFileInfo $pharLocation, SplFileInfo if (null === $this->keys) { throw new RuntimeException('No keys to verify the signature'); } - $factory = new Factory(); - $verify = new Verify( - $this->keys, - $factory->createGnuPG(new Directory(sys_get_temp_dir())) - ); + + $verify = new Verify($this->keys, $this->gpg); if (!$verify->fileWithSignature($pharLocation, $signatureLocation)) { throw new RuntimeException('Signature Verification failed'); From 62334fa575b8b792b7ebb517f98e0f2cc35841b4 Mon Sep 17 00:00:00 2001 From: Sebastian Feldmann Date: Sat, 12 Dec 2020 15:36:51 +0100 Subject: [PATCH 3/4] Remove static property Changed all Yoda conditionals to regular ones. --- src/ConfiguredMediator.php | 6 +----- src/Service/Installer.php | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ConfiguredMediator.php b/src/ConfiguredMediator.php index b7746cc..37b42d1 100644 --- a/src/ConfiguredMediator.php +++ b/src/ConfiguredMediator.php @@ -21,9 +21,6 @@ abstract class ConfiguredMediator extends PluginBase /** @var \PharIo\ComposerDistributor\Config\Config */ private $config; - /** @var bool */ - private static $warningDisplayed = false; - /** * Config has to be loaded on instantiation because on uninstall all external dependencies are * removed before `uninstall` is called and auto-loading any external phar-io dependencies then will fail. @@ -47,7 +44,7 @@ public function installOrUpdateFunction(PackageEvent $event): void $gnuPG = $this->createGnuPG(); // we do not want to crash if no GnuPG was found // but display a noticeable warning to the user - if (null === $gnuPG && !self::$warningDisplayed) { + if ($gnuPG === null) { $this->io->write( PHP_EOL . ' WARNING' . PHP_EOL . @@ -55,7 +52,6 @@ public function installOrUpdateFunction(PackageEvent $event): void ' Consider installing GnuPG to verify PHAR authenticity.' . PHP_EOL . ' If you need help installing GnuPG visit http://phar.io/install-gnupg' . PHP_EOL ); - self::$warningDisplayed = true; } $installer = $this->createInstallerFromConfig($this->config, $event); diff --git a/src/Service/Installer.php b/src/Service/Installer.php index eaaa115..33b1f10 100644 --- a/src/Service/Installer.php +++ b/src/Service/Installer.php @@ -86,7 +86,7 @@ private function downloadAndVerify(VersionConstraintReplacer $versionReplacer, F return $pharLocation; } - if (null === $this->gpg) { + if ($this->gpg === null) { $this->io->write(' - No GnuPG found to verify signature! Use this file with care!'); return $pharLocation; } @@ -122,7 +122,7 @@ private function downloadSignature(VersionConstraintReplacer $versionReplacer, F private function verifyPharWithSignature(SplFileInfo $pharLocation, SplFileInfo $signatureLocation): void { - if (null === $this->keys) { + if ($this->keys === null) { throw new RuntimeException('No keys to verify the signature'); } From 4ce2670d8a63f55318f744e18cee703325ddacc2 Mon Sep 17 00:00:00 2001 From: Sebastian Feldmann Date: Sun, 13 Dec 2020 17:27:59 +0100 Subject: [PATCH 4/4] Make properties private --- src/ConfiguredMediator.php | 13 ++++++------- src/PluginBase.php | 22 +++++++++++++++++++--- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/ConfiguredMediator.php b/src/ConfiguredMediator.php index 37b42d1..e8819e3 100644 --- a/src/ConfiguredMediator.php +++ b/src/ConfiguredMediator.php @@ -34,8 +34,7 @@ abstract protected function getDistributorConfig(): string; public function uninstall(Composer $composer, IOInterface $io) { - $this->composer = $composer; - $this->io = $io; + parent::uninstall($composer, $io); $this->removePhars(); } @@ -45,7 +44,7 @@ public function installOrUpdateFunction(PackageEvent $event): void // we do not want to crash if no GnuPG was found // but display a noticeable warning to the user if ($gnuPG === null) { - $this->io->write( + $this->getIO()->write( PHP_EOL . ' WARNING' . PHP_EOL . ' No GPG installation found! Use installed PHARs with care. ' . PHP_EOL . @@ -77,7 +76,7 @@ private function createInstallerFromConfig(Config $config, PackageEvent $event): { return new Installer( $config->package(), - $this->io, + $this->getIO(), $event ); } @@ -99,7 +98,7 @@ private function createKeyDirectory(Config $config): KeyDirectory private function removePhars(): void { - $binDir = $this->composer->getConfig()->get('bin-dir'); + $binDir = $this->getComposer()->getConfig()->get('bin-dir'); foreach ($this->config->phars()->getList() as $phar) { $this->deleteFile($phar, $binDir); @@ -112,12 +111,12 @@ private function deleteFile(File $phar, string $binDir): void if (is_file($pharLocation)) { if (!is_writable($pharLocation)) { - $this->io->write( + $this->getIO()->write( sprintf(' - Can not remove phar \'%1$s\' (insufficient permissions)', $phar->pharName()) ); return; } - $this->io->write(sprintf(' - Removing phar %1$s', $phar->pharName())); + $this->getIO()->write(sprintf(' - Removing phar %1$s', $phar->pharName())); unlink($pharLocation); } } diff --git a/src/PluginBase.php b/src/PluginBase.php index 65d41a8..05bd366 100644 --- a/src/PluginBase.php +++ b/src/PluginBase.php @@ -15,10 +15,12 @@ abstract class PluginBase implements PluginInterface, EventSubscriberInterface { /** @var \Composer\Composer */ - protected $composer; + private $composer; /** @var \Composer\IO\IOInterface */ - protected $io; + private $io; + + abstract public function installOrUpdateFunction(PackageEvent $event) : void; public function activate(Composer $composer, IOInterface $io) { @@ -59,5 +61,19 @@ public function createInstaller(string $pluginName, PackageEvent $event) : Insta ); } - abstract public function installOrUpdateFunction(PackageEvent $event) : void; + protected function getIO(): IOInterface + { + if (!$this->io) { + throw new \RuntimeException('IO not set'); + } + return $this->io; + } + + protected function getComposer(): Composer + { + if (!$this->composer) { + throw new \RuntimeException('Composer not set'); + } + return $this->composer; + } }