PageRenderTime 93ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Composer/Util/GitLab.php

http://github.com/composer/composer
PHP | 187 lines | 103 code | 34 blank | 50 comment | 15 complexity | 1ee5cc80efe4242b29fba232cedae9d8 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Util;
  12. use Composer\IO\IOInterface;
  13. use Composer\Config;
  14. use Composer\Factory;
  15. use Composer\Downloader\TransportException;
  16. use Composer\Json\JsonFile;
  17. /**
  18. * @author Roshan Gautam <roshan.gautam@hotmail.com>
  19. */
  20. class GitLab
  21. {
  22. /** @var IOInterface */
  23. protected $io;
  24. /** @var Config */
  25. protected $config;
  26. /** @var ProcessExecutor */
  27. protected $process;
  28. /** @var HttpDownloader */
  29. protected $httpDownloader;
  30. /**
  31. * Constructor.
  32. *
  33. * @param IOInterface $io The IO instance
  34. * @param Config $config The composer configuration
  35. * @param ProcessExecutor $process Process instance, injectable for mocking
  36. * @param HttpDownloader $httpDownloader Remote Filesystem, injectable for mocking
  37. */
  38. public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, HttpDownloader $httpDownloader = null)
  39. {
  40. $this->io = $io;
  41. $this->config = $config;
  42. $this->process = $process ?: new ProcessExecutor($io);
  43. $this->httpDownloader = $httpDownloader ?: Factory::createHttpDownloader($this->io, $config);
  44. }
  45. /**
  46. * Attempts to authorize a GitLab domain via OAuth.
  47. *
  48. * @param string $originUrl The host this GitLab instance is located at
  49. *
  50. * @return bool true on success
  51. */
  52. public function authorizeOAuth($originUrl)
  53. {
  54. // before composer 1.9, origin URLs had no port number in them
  55. $bcOriginUrl = preg_replace('{:\d+}', '', $originUrl);
  56. if (!in_array($originUrl, $this->config->get('gitlab-domains'), true) && !in_array($bcOriginUrl, $this->config->get('gitlab-domains'), true)) {
  57. return false;
  58. }
  59. // if available use token from git config
  60. if (0 === $this->process->execute('git config gitlab.accesstoken', $output)) {
  61. $this->io->setAuthentication($originUrl, trim($output), 'oauth2');
  62. return true;
  63. }
  64. // if available use deploy token from git config
  65. if (0 === $this->process->execute('git config gitlab.deploytoken.user', $tokenUser) && 0 === $this->process->execute('git config gitlab.deploytoken.token', $tokenPassword)) {
  66. $this->io->setAuthentication($originUrl, trim($tokenUser), trim($tokenPassword));
  67. return true;
  68. }
  69. // if available use token from composer config
  70. $authTokens = $this->config->get('gitlab-token');
  71. if (isset($authTokens[$originUrl])) {
  72. $token = $authTokens[$originUrl];
  73. }
  74. if (isset($authTokens[$bcOriginUrl])) {
  75. $token = $authTokens[$bcOriginUrl];
  76. }
  77. if(isset($token)){
  78. $username = is_array($token) && array_key_exists("username", $token) ? $token["username"] : $token;
  79. $password = is_array($token) && array_key_exists("token", $token) ? $token["token"] : 'private-token';
  80. $this->io->setAuthentication($originUrl, $username, $password);
  81. return true;
  82. }
  83. return false;
  84. }
  85. /**
  86. * Authorizes a GitLab domain interactively via OAuth.
  87. *
  88. * @param string $scheme Scheme used in the origin URL
  89. * @param string $originUrl The host this GitLab instance is located at
  90. * @param string $message The reason this authorization is required
  91. *
  92. * @throws \RuntimeException
  93. * @throws TransportException|\Exception
  94. *
  95. * @return bool true on success
  96. */
  97. public function authorizeOAuthInteractively($scheme, $originUrl, $message = null)
  98. {
  99. if ($message) {
  100. $this->io->writeError($message);
  101. }
  102. $this->io->writeError(sprintf('A token will be created and stored in "%s", your password will never be stored', $this->config->getAuthConfigSource()->getName()));
  103. $this->io->writeError('To revoke access to this token you can visit '.$originUrl.'/profile/applications');
  104. $attemptCounter = 0;
  105. while ($attemptCounter++ < 5) {
  106. try {
  107. $response = $this->createToken($scheme, $originUrl);
  108. } catch (TransportException $e) {
  109. // 401 is bad credentials,
  110. // 403 is max login attempts exceeded
  111. if (in_array($e->getCode(), array(403, 401))) {
  112. if (401 === $e->getCode()) {
  113. $this->io->writeError('Bad credentials.');
  114. } else {
  115. $this->io->writeError('Maximum number of login attempts exceeded. Please try again later.');
  116. }
  117. $this->io->writeError('You can also manually create a personal token at '.$scheme.'://'.$originUrl.'/profile/personal_access_tokens');
  118. $this->io->writeError('Add it using "composer config --global --auth gitlab-token.'.$originUrl.' <token>"');
  119. continue;
  120. }
  121. throw $e;
  122. }
  123. $this->io->setAuthentication($originUrl, $response['access_token'], 'oauth2');
  124. // store value in user config in auth file
  125. $this->config->getAuthConfigSource()->addConfigSetting('gitlab-oauth.'.$originUrl, $response['access_token']);
  126. return true;
  127. }
  128. throw new \RuntimeException('Invalid GitLab credentials 5 times in a row, aborting.');
  129. }
  130. private function createToken($scheme, $originUrl)
  131. {
  132. $username = $this->io->ask('Username: ');
  133. $password = $this->io->askAndHideAnswer('Password: ');
  134. $headers = array('Content-Type: application/x-www-form-urlencoded');
  135. $apiUrl = $originUrl;
  136. $data = http_build_query(array(
  137. 'username' => $username,
  138. 'password' => $password,
  139. 'grant_type' => 'password',
  140. ), null, '&');
  141. $options = array(
  142. 'retry-auth-failure' => false,
  143. 'http' => array(
  144. 'method' => 'POST',
  145. 'header' => $headers,
  146. 'content' => $data,
  147. ),
  148. );
  149. $token = $this->httpDownloader->get($scheme.'://'.$apiUrl.'/oauth/token', $options)->decodeJson();
  150. $this->io->writeError('Token successfully created');
  151. return $token;
  152. }
  153. }