/**
  * {@inheritDoc}
  */
 public function activate(Composer $composer, IOInterface $io)
 {
     $this->composer = $composer;
     $this->io = $io;
     // setting the authentication credentials here as the onPreFileDownload event
     // does not get fired when accessing the packages.json file of a satis repository.
     $authConfig = $this->getAuthConfig();
     foreach ($authConfig as $host => $credentials) {
         if (!isset($credentials['username'])) {
             continue;
         }
         if (!isset($credentials['password'])) {
             $credentials['password'] = null;
         }
         // set the authentication credentials for each host
         $this->io->setAuthentication($host, $credentials['username'], $credentials['password']);
         // for a satis repository we need to explicitly add the authentication
         // information for the packages.json file. Otherwise the user still needs
         // to enter the credentials.
         $this->io->setAuthentication('http://' . $host . '/packages.json', $credentials['username'], $credentials['password']);
         $this->io->setAuthentication('https://' . $host . '/packages.json', $credentials['username'], $credentials['password']);
     }
     $this->io->write('<error>AuthStore plugin is deprecated. Use the built-in functionality in Composer instead!</error>');
 }
Example #2
0
 /**
  * normalize url and authentication info
  * @param string $origin domain text
  * @param string $url
  * @param IO/IOInterface $io
  */
 public function __construct($origin, $url, IO\IOInterface $io)
 {
     // normalize github origin
     if (substr($origin, -10) === 'github.com') {
         $origin = 'github.com';
         $this->special = 'github';
     }
     $this->origin = $origin;
     $this->importURL($url);
     if ($this->username && $this->password) {
         $io->setAuthentication($origin, $this->username, $this->password);
     } elseif ($io->hasAuthentication($origin)) {
         $auth = $io->getAuthentication($origin);
         $this->username = $auth['username'];
         $this->password = $auth['password'];
     }
 }
Example #3
0
 public function runCommand($commandCallable, $url, $cwd, $initialClone = false)
 {
     if (preg_match('{^(http|git):}i', $url) && $this->config->get('secure-http')) {
         throw new TransportException("Your configuration does not allow connection to {$url}. See https://getcomposer.org/doc/06-config.md#secure-http for details.");
     }
     if ($initialClone) {
         $origCwd = $cwd;
         $cwd = null;
     }
     if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $url)) {
         throw new \InvalidArgumentException('The source URL ' . $url . ' is invalid, ssh URLs should have a port number after ":".' . "\n" . 'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.');
     }
     if (!$initialClone) {
         // capture username/password from URL if there is one
         $this->process->execute('git remote -v', $output, $cwd);
         if (preg_match('{^(?:composer|origin)\\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) {
             $this->io->setAuthentication($match[3], urldecode($match[1]), urldecode($match[2]));
         }
     }
     $protocols = $this->config->get('github-protocols');
     if (!is_array($protocols)) {
         throw new \RuntimeException('Config value "github-protocols" must be an array, got ' . gettype($protocols));
     }
     // public github, autoswitch protocols
     if (preg_match('{^(?:https?|git)://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match)) {
         $messages = array();
         foreach ($protocols as $protocol) {
             if ('ssh' === $protocol) {
                 $protoUrl = "git@" . $match[1] . ":" . $match[2];
             } else {
                 $protoUrl = $protocol . "://" . $match[1] . "/" . $match[2];
             }
             if (0 === $this->process->execute(call_user_func($commandCallable, $protoUrl), $ignoredOutput, $cwd)) {
                 return;
             }
             $messages[] = '- ' . $protoUrl . "\n" . preg_replace('#^#m', '  ', $this->process->getErrorOutput());
             if ($initialClone) {
                 $this->filesystem->removeDirectory($origCwd);
             }
         }
         // failed to checkout, first check git accessibility
         $this->throwException('Failed to clone ' . self::sanitizeUrl($url) . ' via ' . implode(', ', $protocols) . ' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url);
     }
     // if we have a private github url and the ssh protocol is disabled then we skip it and directly fallback to https
     $bypassSshForGitHub = preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\\.git$}i', $url) && !in_array('ssh', $protocols, true);
     $command = call_user_func($commandCallable, $url);
     $auth = null;
     if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $ignoredOutput, $cwd)) {
         // private github repository without git access, try https with auth
         if (preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\\.git$}i', $url, $match)) {
             if (!$this->io->hasAuthentication($match[1])) {
                 $gitHubUtil = new GitHub($this->io, $this->config, $this->process);
                 $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos';
                 if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) {
                     $gitHubUtil->authorizeOAuthInteractively($match[1], $message);
                 }
             }
             if ($this->io->hasAuthentication($match[1])) {
                 $auth = $this->io->getAuthentication($match[1]);
                 $authUrl = 'https://' . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[1] . '/' . $match[2] . '.git';
                 $command = call_user_func($commandCallable, $authUrl);
                 if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) {
                     return;
                 }
             }
         } elseif ($this->isAuthenticationFailure($url, $match)) {
             // private non-github repo that failed to authenticate
             if (strpos($match[2], '@')) {
                 list($authParts, $match[2]) = explode('@', $match[2], 2);
             }
             $storeAuth = false;
             if ($this->io->hasAuthentication($match[2])) {
                 $auth = $this->io->getAuthentication($match[2]);
             } elseif ($this->io->isInteractive()) {
                 $defaultUsername = null;
                 if (isset($authParts) && $authParts) {
                     if (false !== strpos($authParts, ':')) {
                         list($defaultUsername, ) = explode(':', $authParts, 2);
                     } else {
                         $defaultUsername = $authParts;
                     }
                 }
                 $this->io->writeError('    Authentication required (<info>' . parse_url($url, PHP_URL_HOST) . '</info>):');
                 $auth = array('username' => $this->io->ask('      Username: '******'password' => $this->io->askAndHideAnswer('      Password: '******'store-auths');
             }
             if ($auth) {
                 $authUrl = $match[1] . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[2] . $match[3];
                 $command = call_user_func($commandCallable, $authUrl);
                 if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) {
                     $this->io->setAuthentication($match[2], $auth['username'], $auth['password']);
                     $authHelper = new AuthHelper($this->io, $this->config);
                     $authHelper->storeAuth($match[2], $storeAuth);
                     return;
                 }
             }
         }
         if ($initialClone) {
             $this->filesystem->removeDirectory($origCwd);
         }
         $this->throwException('Failed to execute ' . self::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url);
     }
 }
Example #4
0
 /**
  * @param $io
  * @param bool $useRedirector
  * @param $githubDomains
  * @param $gitlabDomains
  */
 protected function setupAuthentication(IO\IOInterface $io, $useRedirector, array $githubDomains, array $gitlabDomains)
 {
     if (preg_match('/\\.github\\.com$/', $this->host)) {
         $authKey = 'github.com';
         if ($useRedirector) {
             if ($this->host === 'api.github.com' && preg_match('%^/repos(/[^/]+/[^/]+/)zipball(.+)$%', $this->path, $_)) {
                 $this->host = 'codeload.github.com';
                 $this->path = $_[1] . 'legacy.zip' . $_[2];
             }
         }
     } else {
         $authKey = $this->host;
     }
     if (!$io->hasAuthentication($authKey)) {
         if ($this->user || $this->pass) {
             $io->setAuthentication($authKey, $this->user, $this->pass);
         } else {
             return;
         }
     }
     $auth = $io->getAuthentication($authKey);
     // is github
     if (in_array($authKey, $githubDomains) && 'x-oauth-basic' === $auth['password']) {
         $this->addParam('access_token', $auth['username']);
         $this->user = $this->pass = null;
         return;
     }
     // is gitlab
     if (in_array($authKey, $gitlabDomains) && 'oauth2' === $auth['password']) {
         $this->addHeader('authorization', 'Bearer ' . $auth['username']);
         $this->user = $this->pass = null;
         return;
     }
     // others, includes bitbucket
     $this->user = $auth['username'];
     $this->pass = $auth['password'];
 }
Example #5
0
 /**
  * Creates a Composer instance
  *
  * @param IOInterface       $io          IO instance
  * @param array|string|null $localConfig either a configuration array or a filename to read from, if null it will
  *                                       read from the default filename
  * @throws \InvalidArgumentException
  * @return Composer
  */
 public function createComposer(IOInterface $io, $localConfig = null)
 {
     // load Composer configuration
     if (null === $localConfig) {
         $localConfig = static::getComposerFile();
     }
     if (is_string($localConfig)) {
         $composerFile = $localConfig;
         $file = new JsonFile($localConfig, new RemoteFilesystem($io));
         if (!$file->exists()) {
             if ($localConfig === 'composer.json') {
                 $message = 'Composer could not find a composer.json file in ' . getcwd();
             } else {
                 $message = 'Composer could not find the config file: ' . $localConfig;
             }
             $instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section';
             throw new \InvalidArgumentException($message . PHP_EOL . $instructions);
         }
         $file->validateSchema(JsonFile::LAX_SCHEMA);
         $localConfig = $file->read();
     }
     // Configuration defaults
     $config = static::createConfig();
     $config->merge($localConfig);
     // reload oauth token from config if available
     if ($tokens = $config->get('github-oauth')) {
         foreach ($tokens as $domain => $token) {
             if (!preg_match('{^[a-z0-9]+$}', $token)) {
                 throw new \UnexpectedValueException('Your github oauth token for ' . $domain . ' contains invalid characters: "' . $token . '"');
             }
             $io->setAuthentication($domain, $token, 'x-oauth-basic');
         }
     }
     $vendorDir = $config->get('vendor-dir');
     $binDir = $config->get('bin-dir');
     // setup process timeout
     ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
     // initialize repository manager
     $rm = $this->createRepositoryManager($io, $config);
     // load local repository
     $this->addLocalRepository($rm, $vendorDir);
     // load package
     $loader = new Package\Loader\RootPackageLoader($rm, $config);
     $package = $loader->load($localConfig);
     // initialize download manager
     $dm = $this->createDownloadManager($io, $config);
     // initialize installation manager
     $im = $this->createInstallationManager();
     // initialize composer
     $composer = new Composer();
     $composer->setConfig($config);
     $composer->setPackage($package);
     $composer->setRepositoryManager($rm);
     $composer->setDownloadManager($dm);
     $composer->setInstallationManager($im);
     // initialize event dispatcher
     $dispatcher = new EventDispatcher($composer, $io);
     $composer->setEventDispatcher($dispatcher);
     // initialize autoload generator
     $generator = new AutoloadGenerator($dispatcher);
     $composer->setAutoloadGenerator($generator);
     // add installers to the manager
     $this->createDefaultInstallers($im, $composer, $io);
     // purge packages if they have been deleted on the filesystem
     $this->purgePackages($rm, $im);
     // init locker if possible
     if (isset($composerFile)) {
         $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) ? substr($composerFile, 0, -4) . 'lock' : $composerFile . '.lock';
         $locker = new Package\Locker(new JsonFile($lockFile, new RemoteFilesystem($io)), $rm, $im, md5_file($composerFile));
         $composer->setLocker($locker);
     }
     return $composer;
 }
 public function promptAuth(HttpGetResponse $res, CConfig $config, IO\IOInterface $io)
 {
     $httpCode = $res->info['http_code'];
     // 404s are only handled for github
     if (404 === $httpCode) {
         return false;
     }
     // fail if the console is not interactive
     if (!$io->isInteractive()) {
         switch ($httpCode) {
             case 401:
                 $message = "The '{$this->getURL()}' URL required authentication.\nYou must be using the interactive console to authenticate";
                 break;
             case 403:
                 $message = "The '{$this->getURL()}' URL could not be accessed.";
                 break;
         }
         throw new Downloader\TransportException($message, $httpCode);
     }
     // fail if we already have auth
     if ($io->hasAuthentication($this->origin)) {
         throw new Downloader\TransportException("Invalid credentials for '{$this->getURL()}', aborting.", $httpCode);
     }
     $io->overwrite("    Authentication required (<info>{$this->host}</info>):");
     $username = $io->ask('      Username: '******'      Password: ');
     $io->setAuthentication($this->origin, $username, $password);
     return true;
 }
Example #7
0
 /**
  * Update a project
  *
  * @param \Packagist\WebBundle\Entity\Package $package
  * @param RepositoryInterface $repository the repository instance used to update from
  * @param int $flags a few of the constants of this class
  * @param \DateTime $start
  */
 public function update(IOInterface $io, Config $config, Package $package, RepositoryInterface $repository, $flags = 0, \DateTime $start = null)
 {
     $rfs = new RemoteFilesystem($io, $config);
     $blacklist = '{^symfony/symfony (2.0.[456]|dev-charset|dev-console)}i';
     if (null === $start) {
         $start = new \DateTime();
     }
     $pruneDate = clone $start;
     $pruneDate->modify('-1min');
     $em = $this->doctrine->getManager();
     $apc = extension_loaded('apcu');
     if ($repository instanceof VcsRepository) {
         $cfg = $repository->getRepoConfig();
         if (isset($cfg['url']) && preg_match('{\\bgithub\\.com\\b}', $cfg['url'])) {
             foreach ($package->getMaintainers() as $maintainer) {
                 if (!($newGithubToken = $maintainer->getGithubToken())) {
                     continue;
                 }
                 $valid = null;
                 if ($apc) {
                     $valid = apcu_fetch('is_token_valid_' . $maintainer->getUsernameCanonical());
                 }
                 if (true !== $valid) {
                     $context = stream_context_create(['http' => ['header' => 'User-agent: packagist-token-check']]);
                     $rate = json_decode(@file_get_contents('https://api.github.com/rate_limit?access_token=' . $newGithubToken, false, $context), true);
                     // invalid/outdated token, wipe it so we don't try it again
                     if (!$rate && (strpos($http_response_header[0], '403') || strpos($http_response_header[0], '401'))) {
                         $maintainer->setGithubToken(null);
                         $em->flush($maintainer);
                         continue;
                     }
                 }
                 if ($apc) {
                     apcu_store('is_token_valid_' . $maintainer->getUsernameCanonical(), true, 86400);
                 }
                 $io->setAuthentication('github.com', $newGithubToken, 'x-oauth-basic');
                 break;
             }
         }
     }
     $versions = $repository->getPackages();
     usort($versions, function ($a, $b) {
         $aVersion = $a->getVersion();
         $bVersion = $b->getVersion();
         if ($aVersion === '9999999-dev' || 'dev-' === substr($aVersion, 0, 4)) {
             $aVersion = 'dev';
         }
         if ($bVersion === '9999999-dev' || 'dev-' === substr($bVersion, 0, 4)) {
             $bVersion = 'dev';
         }
         $aIsDev = $aVersion === 'dev' || substr($aVersion, -4) === '-dev';
         $bIsDev = $bVersion === 'dev' || substr($bVersion, -4) === '-dev';
         // push dev versions to the end
         if ($aIsDev !== $bIsDev) {
             return $aIsDev ? 1 : -1;
         }
         // equal versions are sorted by date
         if ($aVersion === $bVersion) {
             return $a->getReleaseDate() > $b->getReleaseDate() ? 1 : -1;
         }
         // the rest is sorted by version
         return version_compare($aVersion, $bVersion);
     });
     $versionRepository = $this->doctrine->getRepository('PackagistWebBundle:Version');
     if ($flags & self::DELETE_BEFORE) {
         foreach ($package->getVersions() as $version) {
             $versionRepository->remove($version);
         }
         $em->flush();
         $em->refresh($package);
     }
     $lastUpdated = true;
     $lastProcessed = null;
     foreach ($versions as $version) {
         if ($version instanceof AliasPackage) {
             continue;
         }
         if (preg_match($blacklist, $version->getName() . ' ' . $version->getPrettyVersion())) {
             continue;
         }
         if ($lastProcessed && $lastProcessed->getVersion() === $version->getVersion()) {
             $io->write('Skipping version ' . $version->getPrettyVersion() . ' (duplicate of ' . $lastProcessed->getPrettyVersion() . ')', true, IOInterface::VERBOSE);
             continue;
         }
         $lastProcessed = $version;
         $lastUpdated = $this->updateInformation($package, $version, $flags);
         if ($lastUpdated) {
             $em->flush();
         }
     }
     if (!$lastUpdated) {
         $em->flush();
     }
     // remove outdated versions
     foreach ($package->getVersions() as $version) {
         if ($version->getUpdatedAt() < $pruneDate) {
             $versionRepository->remove($version);
         }
     }
     if (preg_match('{^(?:git://|git@|https?://)github.com[:/]([^/]+)/(.+?)(?:\\.git|/)?$}i', $package->getRepository(), $match) && $repository instanceof VcsRepository) {
         $this->updateGitHubInfo($rfs, $package, $match[1], $match[2], $repository);
     }
     $package->setUpdatedAt(new \DateTime());
     $package->setCrawledAt(new \DateTime());
     $em->flush();
     if ($repository->hadInvalidBranches()) {
         throw new InvalidRepositoryException('Some branches contained invalid data and were discarded, it is advised to review the log and fix any issues present in branches');
     }
 }