Exemplo n.º 1
0
    def _check_api_error(self, e):
        data = e.read()

        try:
            rsp = json.loads(data)
        except:
            rsp = None

        message = data

        if rsp and 'error' in rsp:
            error = rsp['error']

            if 'message' in error:
                message = error['message']

        if message:
            message = six.text_type(message)

        if e.code == 401:
            raise AuthorizationError(
                message or ugettext('Invalid Bitbucket username or password'))
        elif e.code == 404:
            if message.startswith('Repository'):
                raise HostingServiceError(message)

            # We don't have a path here, but it will be filled in inside
            # _api_get_src.
            raise FileNotFoundError('')
        else:
            raise HostingServiceError(
                message or ugettext('Unknown error when talking to Bitbucket'))
Exemplo n.º 2
0
    def authorize(self, username, credentials, hosting_url, *args, **kwargs):
        """Authorize the GitLab repository.

        GitLab uses HTTP Basic Auth for the API, so this will store the
        provided password, encrypted, for use in later API requests.

        Args:
            username (unicode):
                The username of the account being linked.

            credentials (dict):
                Authentication credentials.

            hosting_url (unicode):
                The URL of the GitLab server.

            *args (tuple, unused):
                Ignored positional arguments.

            **kwargs (dict, unused):
                Ignored keyword arguments.

        Raises:
            reviewboard.hostingsvcs.errors.AuthorizationError:
                Authorization could not be completed successfully.

            reviewboard.hostingsvcs.errors.HostingServiceError:
                An HTTP or other unexpected error occurred.
        """
        # This will raise an exception if it fails, which the form will
        # catch.
        try:
            self._try_api_versions(hosting_url,
                                   path='/projects?per_page=1',
                                   headers={
                                       'PRIVATE-TOKEN':
                                       credentials['private_token'],
                                   })
        except (AuthorizationError, GitLabAPIVersionError):
            raise
        except HTTPError as e:
            if e.code == 404:
                raise HostingServiceError(
                    ugettext('A GitLab server was not found at the '
                             'provided URL.'))
            else:
                logging.exception(
                    'Unexpected HTTP error when linking GitLab '
                    'account for %s: %s', username, e)
                raise HostingServiceError(
                    ugettext('Unexpected HTTP error %s.') % e.code)
        except Exception as e:
            logging.exception(
                'Unexpected error when linking GitLab account '
                'for %s: %s', username, e)
            raise HostingServiceError(ugettext('Unexpected error "%s"') % e)

        self.account.data['private_token'] = \
            encrypt_password(credentials['private_token'])
        self.account.save()
Exemplo n.º 3
0
    def process_http_error(self, request, e):
        """Process an HTTP error, converting to a HostingServiceError.

        Args:
            request (reviewboard.hostingsvcs.service.
                     HostingServiceHTTPRequest):
                The request that resulted in an error.

            e (urllib2.URLError):
                The error to process.

        Raises:
            reviewboard.hostingsvcs.errors.HostingServiceAPIError:
                An error occurred communicating with the Gerrit API. A payload
                is available.

            reviewboard.hostingsvcs.errors.HostingServiceError:
                An error occurred communicating with the Gerrit API. A payload
                is not available.

            reviewboard.scmtools.errors.UnverifiedCertificateError:
                The SSL certificate was not able to be verified.
        """
        super(GerritClient, self).process_http_error(request, e)

        if isinstance(e, HTTPError):
            code = e.getcode()

            try:
                raise HostingServiceAPIError(e.reason, code, e.read())
            except AttributeError:
                raise HostingServiceError(e.reason, code)
        elif isinstance(e, URLError):
            raise HostingServiceError(e.reason)
Exemplo n.º 4
0
    def http_request(self,
                     url,
                     body=None,
                     headers=None,
                     method='GET',
                     username=None,
                     password=None):
        """Make an HTTP request to the given URL and return the result.

        This method requires both the username and password arguments to be
        passed since all Gerrit API endpoints require authentication.

        Args:
            url (unicode):
                The URL to make the request against.

            body (unicode, optional):
                The request body.

            headers (dict, optional):
                Additional headers to include in the request.

            method (unicode, optional):
                The HTTP method to use for the request. This defaults to GET.

            username (unicode):
                The username to use for authentication.

            password (unicode):
                The password to use for authentication.

        Returns:
            tuple:
            A 2-tuple of:

            * The response body (:py:class:`bytes`).
            * The response headers (:py:class:`dict`).
        """
        assert username is not None
        assert password is not None

        opener = self.get_opener(url, username, password)
        request = URLRequest(url, body, headers, method=method)

        # Gerrit 2.14+ require Basic Auth, so add that header. Old versions
        # use Digest Auth, which get_opener() already prepared.
        if username is not None and password is not None:
            request.add_basic_auth(username, password)

        try:
            response = opener.open(request)
        except HTTPError as e:
            try:
                raise HostingServiceAPIError(e.reason, e.getcode(), e.read())
            except AttributeError:
                raise HostingServiceError(e.reason, e.getcode())
        except URLError as e:
            raise HostingServiceError(e.reason)

        return response.read(), response.headers
Exemplo n.º 5
0
    def _check_api_error(self, e):
        data = e.read()

        try:
            rsp = json.loads(data.decode('utf-8'))
        except:
            rsp = None

        if rsp and 'message' in rsp:
            if e.code == 401:
                raise AuthorizationError(rsp['message'], http_code=e.code)

            raise HostingServiceError(rsp['message'], http_code=e.code)
        else:
            raise HostingServiceError(six.text_type(e), http_code=e.code)
Exemplo n.º 6
0
    def authorize(self, username, password, hosting_url, *args, **kwargs):
        """Authorize the Review Board Gateway repository.

        Review Board Gateway uses HTTP Basic Auth, so this will store the
        provided password, encrypted, for use in later API requests.

        Similar to GitLab's API, Review Board Gateway will return a private
        token on session authentication.
        """
        try:
            response = self.client.http_post(url='%s/session' % hosting_url,
                                             username=username,
                                             password=password)
        except HTTPError as e:
            if e.code == 401:
                raise AuthorizationError(
                    ugettext('The username or password is incorrect.'))
            elif e.code == 404:
                raise HostingServiceError(
                    ugettext('A Review Board Gateway server was not found at '
                             'the provided URL.'))
            else:
                logger.exception('Failed authorization at %s/session: %s',
                                 hosting_url, e)

            raise

        self.account.data['private_token'] = \
            encrypt_password(response.json['private_token'])
        self.account.save()
Exemplo n.º 7
0
    def _check_api_error(self, e):
        data = e.read()

        try:
            rsp = json.loads(data)
        except:
            rsp = None

        message = data

        if rsp and 'error' in rsp:
            error = rsp['error']

            if 'message' in error:
                message = error['message']

        if message:
            message = six.text_type(message)

        if e.code == 401:
            raise AuthorizationError(
                message or ugettext('Invalid Bitbucket username or password'))
        else:
            raise HostingServiceError(
                message or ugettext('Unknown error when talking to Bitbucket'))
Exemplo n.º 8
0
    def _check_api_error(self, e):
        data = e.read()

        try:
            rsp = json.loads(data)
        except:
            rsp = None

        message = data

        if rsp and 'error' in rsp:
            error = rsp['error']

            if 'message' in error:
                message = error['message']

        if message:
            message = six.text_type(message)

        if e.code == 401:
            self._raise_auth_error(message)
        elif e.code == 404:
            if message.startswith('Repository'):
                raise HostingServiceError(message, http_code=e.code)

            # We don't have a path here, but it will be filled in inside
            # _api_get_src.
            raise FileNotFoundError('')
        else:
            raise HostingServiceAPIError(
                message
                or (ugettext('Unexpected HTTP %s error when talking to '
                             'Bitbucket') % e.code),
                http_code=e.code,
                rsp=e)
Exemplo n.º 9
0
    def authorize(self, username, password, hosting_url, *args, **kwargs):
        """Authorizes the GitLab repository.

        GitLab uses HTTP Basic Auth for the API, so this will store the
        provided password, encrypted, for use in later API requests.
        """
        # This will raise an exception if it fails, which the form will
        # catch.
        try:
            rsp, headers = self._json_post(url=self._build_api_url(
                hosting_url, 'session'),
                                           fields={
                                               'login': username,
                                               'password': password,
                                           })
        except HTTPError as e:
            if e.code == 404:
                raise HostingServiceError(
                    ugettext('A GitLab server was not found at the '
                             'provided URL.'))
            elif e.code == 401:
                raise AuthorizationError(
                    ugettext('The username or password is incorrect.'))
            else:
                raise

        self.account.data['private_token'] = \
            encrypt_password(rsp['private_token'])
        self.account.save()
Exemplo n.º 10
0
    def process_http_error(self, request, e):
        """Process an HTTP error, possibly raising a result.

        This will look at the error, possibly raising a more suitable exception
        in its place. It checks for SSL verification failures, bad credentials,
        and GitHub error payloads.

        Args:
            request (reviewboard.hostingsvcs.service.
                     HostingServiceHTTPRequest):
                The request that resulted in an error.

            e (urllib2.URLError):
                The error to process.

        Raises:
            reviewboard.hostingsvcs.errors.AuthorizationError:
                The repository credentials are invalid.

            reviewboard.hostingsvcs.errors.HostingServiceError:
                There was an error with the request. Details are in the
                response.

            reviewboard.scmtools.errors.UnverifiedCertificateError:
                The SSL certificate was not able to be verified.
        """
        super(GitHubClient, self).process_http_error(request, e)

        try:
            data = e.read()
            rsp = json.loads(data.decode('utf-8'))
        except Exception:
            rsp = None

        if rsp and 'message' in rsp:
            message = rsp['message']

            if e.code == 401:
                raise AuthorizationError(message, http_code=e.code)

            raise HostingServiceError(message, http_code=e.code)
        else:
            raise HostingServiceError(six.text_type(e), http_code=e.code)
Exemplo n.º 11
0
    def authorize(self, username, password, hosting_url, *args, **kwargs):
        """Authorizes the GitLab repository.

        GitLab uses HTTP Basic Auth for the API, so this will store the
        provided password, encrypted, for use in later API requests.
        """
        if self._is_email(username):
            login_key = 'email'
        else:
            login_key = 'login'

        # This will raise an exception if it fails, which the form will
        # catch.
        try:
            rsp, headers = self.client.json_post(url=self._build_api_url(
                hosting_url, 'session'),
                                                 fields={
                                                     login_key: username,
                                                     'password': password,
                                                 })
        except HTTPError as e:
            if e.code == 404:
                raise HostingServiceError(
                    ugettext('A GitLab server was not found at the '
                             'provided URL.'))
            elif e.code == 401:
                raise AuthorizationError(
                    ugettext('The username or password is incorrect.'))
            else:
                logging.exception(
                    'Unexpected HTTP error when linking GitLab '
                    'account for %s: %s', username, e)
                raise HostingServiceError(
                    ugettext('Unexpected HTTP error %s.') % e.code)
        except Exception as e:
            logging.exception(
                'Unexpected error when linking GitLab account '
                'for %s: %s', username, e)
            raise HostingServiceError(ugettext('Unexpected error "%s"') % e)

        self.account.data['private_token'] = \
            encrypt_password(rsp['private_token'])
        self.account.save()
Exemplo n.º 12
0
    def get_commits(self, branch=None, start=None):
        if branch == 'bad:hosting-service-error':
            raise HostingServiceError('This is a HostingServiceError')
        elif branch == 'bad:scm-error':
            raise SCMError('This is a SCMError')

        return [
            Commit('user%d' % i, str(i),
                   '2013-01-01T%02d:00:00.0000000' % i, 'Commit %d' % i,
                   str(i - 1)) for i in range(int(start or 10), 0, -1)
        ]
Exemplo n.º 13
0
    def _check_api_error(self, e):
        data = e.read()

        try:
            rsp = json.loads(data)
        except:
            rsp = None

        if rsp and 'message' in rsp:
            response_info = e.info()
            x_github_otp = response_info.get('X-GitHub-OTP', '')

            if x_github_otp.startswith('required;'):
                raise TwoFactorAuthCodeRequiredError(
                    _('Enter your two-factor authentication code. '
                      'This code will be sent to you by GitHub.'))

            if e.code == 401:
                raise AuthorizationError(rsp['message'])

            raise HostingServiceError(rsp['message'])
        else:
            raise HostingServiceError(six.text_type(e))
Exemplo n.º 14
0
    def process_http_error(self, request, e):
        """Process an HTTP error, raising a result.

        This will look at the error, raising a more suitable exception
        in its place.

        Args:
            request (reviewboard.hostingsvcs.service.HostingServiceHTTPRequest,
                     unused):
                The request that resulted in an error.

            e (urllib2.URLError):
                The error to check.

        Raises:
            reviewboard.hostingsvcs.errors.AuthorizationError:
                The credentials provided were not valid.

            reviewboard.hostingsvcs.errors.HostingServiceAPIError:
                An error occurred communicating with the API. An unparsed
                payload is available.

            reviewboard.hostingsvcs.errors.HostingServiceError:
                There was an unexpected error performing the request.

            reviewboard.scmtools.errors.UnverifiedCertificateError:
                The SSL certificate was not able to be verified.
        """
        # Perform any default checks.
        super(ReviewBoardGatewayClient, self).process_http_error(request, e)

        if isinstance(e, HTTPError):
            code = e.getcode()

            if e.code == 401:
                raise AuthorizationError(
                    ugettext('The username or password is incorrect.'))
            elif e.code == 404:
                raise HostingServiceAPIError(
                    ugettext('The API endpoint was not found.'),
                    http_code=code)
            else:
                msg = e.read()

                raise HostingServiceAPIError(msg, http_code=code, rsp=msg)
        else:
            raise HostingServiceError(e.reason)
Exemplo n.º 15
0
    def get_change(self, commit_id):
        if commit_id == 'bad:hosting-service-error':
            raise HostingServiceError('This is a HostingServiceError')
        elif commit_id == 'bad:scm-error':
            raise SCMError('This is a SCMError')

        return Commit(author_name='user1',
                      id=commit_id,
                      date='2013-01-01T00:00:00.0000000',
                      message='Commit summary\n\nCommit description.',
                      diff=b'\n'.join([
                          b"diff --git a/FILE_FOUND b/FILE_FOUND",
                          b"index 712544e4343bf04967eb5ea80257f6c64d6f42c7.."
                          b"f88b7f15c03d141d0bb38c8e49bb6c411ebfe1f1 100644",
                          b"--- a/FILE_FOUND",
                          b"+++ b/FILE_FOUND",
                          b"@ -1,1 +1,1 @@",
                          b"-blah blah",
                          b"+blah",
                          b"-",
                          b"1.7.1",
                      ]))
Exemplo n.º 16
0
    def get_change(self, repository, revision):
        """Fetch a single commit from GitLab.

        Args:
            repository (reviewboard.scmtools.models.Repository):
                The repository in question.

            revision (unicode):
                The SHA1 hash of the commit to fetch.

        Returns:
            reviewboard.scmtools.core.Commit:
            The commit in question.

        Raises:
            reviewboard.scmtools.errors.AuthorizationError:
                There was an issue with the authorization credentials.

            reviewboard.hostingsvcs.errors.HostingServiceError:
                There was an error fetching information on the commit or
                diff.

            urllib2.HTTPError:
                There was an error communicating with the server.
        """
        repo_api_url = self._get_repo_api_url(repository)
        private_token = self._get_private_token()

        # Step 1: Fetch the commit itself that we want to review, to get
        # the parent SHA and the commit message. Hopefully this information
        # is still in cache so we don't have to fetch it again. However, the
        # parent SHA is probably empty.
        commit = cache.get(repository.get_commit_cache_key(revision))

        if commit is None:
            commit_api_url = ('%s/repository/commits/%s' %
                              (repo_api_url, revision))

            # This response from GitLab consists of one dict type commit and
            # on instance type header object. Only the first element is needed.
            commit_data = self._api_get(repository.hosting_account.hosting_url,
                                        commit_api_url)[0]

            commit = self._parse_commit(commit_data)
            commit.parent = commit_data['parent_ids'][0]

        # Step 2: Get the diff. The revision is the commit header in here.
        # Firstly, a diff url should be built up, which has the format of
        # <hosting_url>/<user-name>/<project-name>/commit/<revision>.diff,
        # then append the private_token to the end of the url and get the diff.

        hosting_url = self.account.hosting_url

        if not hosting_url.endswith('/'):
            hosting_url += '/'

        # Get the project path with the namespace.
        path_api_url = ('%s?private_token=%s' % (repo_api_url, private_token))
        project = self._api_get(repository.hosting_account.hosting_url,
                                path_api_url)[0]
        path_with_namespace = project['path_with_namespace']

        # Build up diff url and get diff.
        diff_url = (
            '%s%s/commit/%s.diff?private_token=%s' %
            (hosting_url, path_with_namespace, revision, private_token))

        try:
            response = self.client.http_get(diff_url,
                                            headers={
                                                'Accept': 'text/plain',
                                                'PRIVATE-TOKEN': private_token,
                                            })
        except HTTPError as e:
            if e.code in (401, 403, 404):
                kb_url = get_kb_url(3000100782)

                raise HostingServiceError(
                    _('Review Board cannot post commits from private '
                      'repositories with this version of GitLab, due to '
                      'GitLab API limitations. Please post this commit '
                      '(%(commit)s) with RBTools. See %(kb_url)s.') % {
                          'commit': revision,
                          'kb_url': kb_url,
                      },
                    help_link=kb_url,
                    help_link_text=_('Learn more'))
            else:
                raise HostingServiceError(
                    _('Unable to fetch the diff for this commit. Received '
                      'an HTTP %(http_code)s error, saying: %(error)s') % {
                          'http_code': e.code,
                          'error': e,
                      })

        diff = response.data

        # Remove the last two lines. The last line is 'libgit <version>',
        # and the second last line is '--', ending with '\n'. To avoid the
        # error from parsing the empty file (size is 0), split the string into
        # two parts using the delimiter '--\nlibgit'. If only use '\n' or '--'
        # delimiter, more characters might be stripped out from file
        # modification commit diff.
        diff = diff.rsplit(b'--\nlibgit', 2)[0]

        # Make sure there's a trailing newline.
        if not diff.endswith(b'\n'):
            diff += b'\n'

        commit.diff = diff
        return commit
Exemplo n.º 17
0
    def _check_api_error_from_exception(self, e):
        self._check_api_error(e.read(), raw_content=True)

        # No error was raised, so raise a default one.
        raise HostingServiceError(six.text_type(e))
Exemplo n.º 18
0
 def _http_request(client, *args, **kwargs):
     raise HostingServiceError('', http_code=401)