Пример #1
0
class JiraConfigureViewErrorsTest(JiraConfigureViewTestCase):
    @patch(
        "sentry.integrations.jira.configure.get_integration_from_request",
        side_effect=AtlassianConnectValidationError(),
    )
    def test_atlassian_connect_validation_error_get(
            self, mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert PERMISSIONS_WARNING in response.content

    @patch(
        "sentry.integrations.jira.configure.get_integration_from_request",
        side_effect=ExpiredSignatureError(),
    )
    def test_expired_signature_error_get(self,
                                         mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert REFRESH_REQUIRED in response.content

    @patch("sentry.integrations.jira.configure.get_integration_from_request")
    def test_user_not_logged_in_get(self, mock_get_integration_from_request):
        mock_get_integration_from_request.return_value = self.installation.model
        response = self.client.get(self.path)

        assert response.status_code == 200
        assert LOGIN_REQUIRED in response.content
        assert absolute_uri(
            reverse("sentry-login")).encode("utf-8") in response.content

    @patch(
        "sentry.integrations.jira.configure.get_integration_from_request",
        side_effect=AtlassianConnectValidationError(),
    )
    def test_atlassian_connect_validation_error_post(
            self, mock_get_integration_from_request):
        response = self.client.post(self.path)
        assert response.status_code == 200
        assert PERMISSIONS_WARNING in response.content

    @patch(
        "sentry.integrations.jira.configure.get_integration_from_request",
        side_effect=ExpiredSignatureError(),
    )
    def test_expired_signature_error_post(self,
                                          mock_get_integration_from_request):
        response = self.client.post(self.path)
        assert response.status_code == 200
        assert REFRESH_REQUIRED in response.content

    @patch("sentry.integrations.jira.configure.get_integration_from_request")
    def test_user_not_logged_in_post(self, mock_get_integration_from_request):
        mock_get_integration_from_request.return_value = self.installation.model
        response = self.client.post(self.path)
        assert response.status_code == 200
        assert LOGIN_REQUIRED in response.content
        assert absolute_uri(
            reverse("sentry-login")).encode("utf-8") in response.content
Пример #2
0
def refresh_token(access_token):
    validation_result, open_token = verify_token(access_token, verify=False)

    current_exp_date = datetime.utcfromtimestamp(open_token["exp"])
    current_utc_date = datetime.utcnow()
    diff_in_seconds = (current_exp_date - current_utc_date).total_seconds()

    if 0 < diff_in_seconds <= app.config["JWT_EXPIRATION_IN_SECONDS"]:
        new_expiration = datetime.utcnow() + timedelta(seconds=app.config["JWT_EXPIRATION_IN_SECONDS"])
        open_token["exp"] = new_expiration
        return jwt.encode(open_token, app.config["SECRET_KEY"])
    else:
        raise ExpiredSignatureError("Token too old to be refreshed")
Пример #3
0
class JiraUiHookViewErrorsTest(JiraUiHookViewTestCase):
    @patch(
        "sentry.integrations.jira.ui_hook.get_integration_from_request",
        side_effect=ExpiredSignatureError(),
    )
    def test_expired_signature_error(self, mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert REFRESH_REQUIRED in response.content

    @patch(
        "sentry.integrations.jira.ui_hook.get_integration_from_request",
        side_effect=AtlassianConnectValidationError(),
    )
    def test_expired_invalid_installation_error(self, mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert UNABLE_TO_VERIFY_INSTALLATION in response.content
Пример #4
0
def get_token_from_header():
    """Pulls the vLab Auth Token from the HTTP header, and decrypts it.

    :Returns: Dictionary

    :Raises: ExpiredSignatureError
    """
    try:
        serialized_token = request.headers.get('X-Auth')
    except AttributeError:
        # no token in header
        raise ExpiredSignatureError('No auth token in HTTP header')
    else:
        return decode(serialized_token,
                      const.AUTH_TOKEN_PUB_KEY,
                      algorithms=const.AUTH_TOKEN_ALGORITHM,
                      issuer=const.AUTH_TOKEN_ISSUER,
                      leeway=10)  # 10 Seconds fuzzy window for clock skewing
Пример #5
0
class JiraIssueHookTest(APITestCase):
    def setUp(self):
        super(JiraIssueHookTest, self).setUp()
        self.first_seen = datetime(2015, 8, 13, 3, 8, 25, tzinfo=timezone.utc)
        self.last_seen = datetime(2016, 1, 13, 3, 8, 25, tzinfo=timezone.utc)
        self.first_release = self.create_release(project=self.project,
                                                 version="v1.0",
                                                 date_added=self.first_seen)
        self.last_release = self.create_release(project=self.project,
                                                version="v1.1",
                                                date_added=self.last_seen)

        self.group = self.create_group(
            self.project,
            message="Sentry Error",
            first_seen=self.first_seen,
            last_seen=self.last_seen,
            first_release=self.first_release,
        )
        group = self.group
        self.path = absolute_uri(
            u"extensions/jira/issue/{}/".format("APP-123")) + "?xdm_e=base_url"

        self.integration = Integration.objects.create(
            provider="jira",
            name="Example Jira",
            metadata={"base_url": "https://getsentry.atlassian.net"},
        )
        self.integration.add_organization(self.organization, self.user)

        external_issue = ExternalIssue.objects.create(
            organization_id=group.organization.id,
            integration_id=self.integration.id,
            key="APP-123")

        GroupLink.objects.create(
            group_id=group.id,
            project_id=group.project_id,
            linked_type=GroupLink.LinkedType.issue,
            linked_id=external_issue.id,
            relationship=GroupLink.Relationship.references,
        )

        self.login_as(self.user)

    @patch(
        "sentry.integrations.jira.issue_hook.get_integration_from_request",
        side_effect=ExpiredSignatureError(),
    )
    def test_expired_signature_error(self, mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert REFRESH_REQUIRED in response.content

    @patch(
        "sentry.integrations.jira.issue_hook.get_integration_from_request",
        side_effect=AtlassianConnectValidationError(),
    )
    def test_expired_invalid_installation_error(
            self, mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert UNABLE_TO_VERIFY_INSTALLATION in response.content

    @patch.object(Group, "get_last_release")
    @patch("sentry.integrations.jira.issue_hook.get_integration_from_request")
    def test_simple_get(self, mock_get_integration_from_request,
                        mock_get_last_release):
        mock_get_last_release.return_value = self.last_release.version
        mock_get_integration_from_request.return_value = self.integration
        response = self.client.get(self.path)
        assert response.status_code == 200
        resp_content = six.text_type(response.content)
        assert self.group.title in resp_content
        assert self.first_seen.strftime("%b. %d, %Y") in resp_content
        assert self.last_seen.strftime("%b. %d, %Y") in resp_content
        assert self.first_release.version in resp_content
        assert self.last_release.version in resp_content

    @patch("sentry.integrations.jira.issue_hook.get_integration_from_request")
    def test_simple_not_linked(self, mock_get_integration_from_request):
        mock_get_integration_from_request.return_value = self.integration
        path = absolute_uri(
            u"extensions/jira/issue/{}/".format("bad-key")) + "?xdm_e=base_url"
        response = self.client.get(path)
        assert response.status_code == 200
        assert b"This Sentry issue is not linked to a Jira issue" in response.content
Пример #6
0
def decode_jwt(encoded_token,
               secret,
               identity_claim_key,
               csrf_value=None,
               audience=None,
               allow_expired=False,
               issuer=None):
    """
    Decodes an encoded JWT

    :param encoded_token: The encoded JWT string to decode
    :param secret: Secret key used to decode the JWT
    :param identity_claim_key: expected key that contains the identity
    :param csrf_value: Expected double submit csrf value
    :param audience: expected audience in the JWT
    :param issuer: expected issuer in the JWT
    :param allow_expired: Options to ignore exp claim validation in token
    :return: Dictionary containing contents of the JWT
    """
    token = encoded_token
    headers = jwt.get_unverified_headers(token)
    kid = headers['kid']
    # search for the kid in the downloaded public keys
    key_index = -1
    for i in range(len(secret)):
        if kid == secret[i]['kid']:
            key_index = i
            break
    if key_index == -1:
        raise JWTDecodeError("Invalid key attribute: kid")
    # construct the public key
    public_key = jwk.construct(secret[key_index])
    # get the last two sections of the token,
    # message and signature (encoded in base64)
    message, encoded_signature = str(token).rsplit('.', 1)
    # decode the signature
    decoded_signature = base64url_decode(encoded_signature.encode('utf-8'))
    # verify the signature
    if not public_key.verify(message.encode("utf8"), decoded_signature):
        raise JWTDecodeError("Signature verification failed")
    # since we passed the verification, we can now safely
    # use the unverified claims
    data = jwt.get_unverified_claims(token)
    if identity_claim_key not in data:
        raise JWTDecodeError("Missing claim: {}".format(identity_claim_key))
    if not allow_expired and time.time() > data['exp']:
        ctx_stack.top.expired_jwt = token
        raise ExpiredSignatureError("Token has expired")
    # check iss
    if 'iss' not in data:
        data['iss'] = None
    if data['iss'] != issuer:
        raise JWTDecodeError("Missing or invalid issuer")
    # check aud if id_token
    if data['token_use'] == 'id':
        if 'aud' not in data:
            data['aud'] = None
        if data['aud'] != audience:
            raise JWTDecodeError("Missing or invalid audience")
    # check clientid if access_token
    if data['token_use'] == 'access':
        if 'client_id' not in data:
            data['client_id'] = None
        if data['client_id'] != audience:
            raise JWTDecodeError("Missing or invalid audience")
    # check csrf
    if csrf_value:
        if 'csrf' not in data:
            raise JWTDecodeError("Missing claim: csrf")
        if not safe_str_cmp(data['csrf'], csrf_value):
            raise CSRFError("CSRF double submit tokens do not match")
    return data
Пример #7
0
class JiraIssueHookTest(APITestCase):
    def setUp(self):
        super().setUp()
        self.first_seen = datetime(2015, 8, 13, 3, 8, 25, tzinfo=timezone.utc)
        self.last_seen = datetime(2016, 1, 13, 3, 8, 25, tzinfo=timezone.utc)
        self.first_release = self.create_release(project=self.project,
                                                 version="v1.0",
                                                 date_added=self.first_seen)
        self.last_release = self.create_release(project=self.project,
                                                version="v1.1",
                                                date_added=self.last_seen)

        self.group = self.create_group(
            self.project,
            message="Sentry Error",
            first_seen=self.first_seen,
            last_seen=self.last_seen,
            first_release=self.first_release,
        )
        group = self.group
        self.issue_key = "APP-123"
        self.path = absolute_uri(
            f"extensions/jira/issue/{self.issue_key}/") + "?xdm_e=base_url"

        self.integration = Integration.objects.create(
            provider="jira",
            name="Example Jira",
            metadata={
                "base_url": "https://getsentry.atlassian.net",
                "shared_secret": "a-super-secret-key-from-atlassian",
            },
        )
        self.integration.add_organization(self.organization, self.user)

        external_issue = ExternalIssue.objects.create(
            organization_id=group.organization.id,
            integration_id=self.integration.id,
            key=self.issue_key,
        )

        GroupLink.objects.create(
            group_id=group.id,
            project_id=group.project_id,
            linked_type=GroupLink.LinkedType.issue,
            linked_id=external_issue.id,
            relationship=GroupLink.Relationship.references,
        )

        self.login_as(self.user)

        self.properties_key = f"com.atlassian.jira.issue:{JIRA_KEY}:sentry-issues-glance:status"
        self.properties_url = "https://getsentry.atlassian.net/rest/api/3/issue/%s/properties/%s"

    @patch(
        "sentry.integrations.jira.views.issue_hook.get_integration_from_request",
        side_effect=ExpiredSignatureError(),
    )
    def test_expired_signature_error(self, mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert REFRESH_REQUIRED in response.content

    @patch(
        "sentry.integrations.jira.views.issue_hook.get_integration_from_request",
        side_effect=AtlassianConnectValidationError(),
    )
    def test_expired_invalid_installation_error(
            self, mock_get_integration_from_request):
        response = self.client.get(self.path)
        assert response.status_code == 200
        assert UNABLE_TO_VERIFY_INSTALLATION.encode() in response.content

    @patch.object(Group, "get_last_release")
    @patch(
        "sentry.integrations.jira.views.issue_hook.get_integration_from_request"
    )
    @responses.activate
    def test_simple_get(self, mock_get_integration_from_request,
                        mock_get_last_release):
        responses.add(responses.PUT,
                      self.properties_url %
                      (self.issue_key, self.properties_key),
                      json={})

        mock_get_last_release.return_value = self.last_release.version
        mock_get_integration_from_request.return_value = self.integration
        response = self.client.get(self.path)
        assert response.status_code == 200
        resp_content = str(response.content)
        assert self.group.title in resp_content
        assert self.first_seen.strftime("%b. %d, %Y") in resp_content
        assert self.last_seen.strftime("%b. %d, %Y") in resp_content
        assert self.first_release.version in resp_content
        assert self.last_release.version in resp_content

    @patch(
        "sentry.integrations.jira.views.issue_hook.get_integration_from_request"
    )
    @responses.activate
    def test_simple_not_linked(self, mock_get_integration_from_request):
        issue_key = "bad-key"
        responses.add(responses.PUT,
                      self.properties_url % (issue_key, self.properties_key),
                      json={})

        mock_get_integration_from_request.return_value = self.integration
        path = absolute_uri(
            "extensions/jira/issue/bad-key/") + "?xdm_e=base_url"
        response = self.client.get(path)
        assert response.status_code == 200
        assert b"This Sentry issue is not linked to a Jira issue" in response.content