Repository: getsentry/sentry

Issue:
VSTS Integration: Internal Server Error
We are currently trialing sentry with a hope to get it connected to our Azure DevOps project(s). However, we are seeing the following when our Organisation Owner attempts to install the integration. 13th August '19 16:35 **Add Installation** while logged into https://sodalabs.visualstudio.com failed with the following. The system status was fine.

![Screenshot 2019-08-14 at 18 46 33](https://user-images.githubusercontent.com/5552649/63043309-def4dc00-bec3-11e9-8fcc-b9086f0b083f.png)

The owner details do appear to return correctly when we check against the API. 
https://app.vssps.visualstudio.com/_apis/accounts?ownerId=<owner_id>&api-version=4.1 

```
{"count":1,"value":[{"accountId":"<account_id>","accountUri":"https://sodalabs.vssps.visualstudio.com:443/","accountName":"sodalabs","properties":{}}]}
```


Discussion:
Internally we got a non-200 response code for that API call which is why the connection failed. I'll fix the fatal error, but I don't know why Azure if giving us a status code other than 200.
You'll also want to make sure that the account completing the integration can create webhook subscriptions. I found another error related to that matching sodalabs.

Resolving patch:
diff --git a/src/sentry/integrations/vsts/integration.py b/src/sentry/integrations/vsts/integration.py
--- a/src/sentry/integrations/vsts/integration.py
+++ b/src/sentry/integrations/vsts/integration.py
@@ -396,7 +396,9 @@ def create_subscription(self, instance, oauth_data):
                 instance, oauth_data, self.oauth_redirect_url
             )
         except ApiError as e:
-            if e.code == 400 or e.code == 403 or "permission" in e.message:
+            auth_codes = (400, 401, 403)
+            permission_error = "permission" in e.message or "not authorized" in e.message
+            if e.code in auth_codes or permission_error:
                 raise IntegrationError(
                     "You do not have sufficient account access to create webhooks "
                     "on the selected Azure DevOps organization.\n"


Test patch:
diff --git a/tests/sentry/integrations/vsts/test_integration.py b/tests/sentry/integrations/vsts/test_integration.py
--- a/tests/sentry/integrations/vsts/test_integration.py
+++ b/tests/sentry/integrations/vsts/test_integration.py
@@ -176,13 +176,47 @@ def test_build_integration(self):
             VSTSIdentityProvider.oauth_scopes
         )
 
-    def test_build_integration__create_subscription_error(self):
+    def test_build_integration__create_subscription_forbidden(self):
         responses.replace(
             responses.POST,
             u"https://{}.visualstudio.com/_apis/hooks/subscriptions".format(
                 self.vsts_account_name.lower()
             ),
             status=403,
+            json={
+                "$id": 1,
+                "message": "The user bob is does not have permission to access this resource",
+                "typeKey": "UnauthorizedRequestException",
+                "errorCode": 0,
+                "eventId": 3000,
+            },
+        )
+        state = {
+            "account": {"accountName": self.vsts_account_name, "accountId": self.vsts_account_id},
+            "base_url": self.vsts_base_url,
+            "identity": {
+                "data": {
+                    "access_token": self.access_token,
+                    "expires_in": "3600",
+                    "refresh_token": self.refresh_token,
+                    "token_type": "jwt-bearer",
+                }
+            },
+        }
+
+        integration = VstsIntegrationProvider()
+
+        with pytest.raises(IntegrationError) as err:
+            integration.build_integration(state)
+        assert "sufficient account access to create webhooks" in six.text_type(err)
+
+    def test_build_integration__create_subscription_unauthorized(self):
+        responses.replace(
+            responses.POST,
+            u"https://{}.visualstudio.com/_apis/hooks/subscriptions".format(
+                self.vsts_account_name.lower()
+            ),
+            status=401,
             json={
                 "$id": 1,
                 "message": "The user bob is not authorized to access this resource",