Repository: jupyterhub/oauthenticator

Issue:
GitLab auth fails for non admin users
Recent changes in GitLab's API have removed the "is_admin" property from most API calls: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10846

Now it is only shown when the request comes from an admin user:
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12211

This causes an error 500 for non-admin users, when "is_admin" is not found in the response.

Discussion:

Patch:
diff --git a/oauthenticator/gitlab.py b/oauthenticator/gitlab.py
index 860aa92..5afddb8 100644
--- a/oauthenticator/gitlab.py
+++ b/oauthenticator/gitlab.py
@@ -107,7 +107,7 @@ class GitLabOAuthenticator(OAuthenticator):
 
         username = resp_json["username"]
         user_id = resp_json["id"]
-        is_admin = resp_json["is_admin"]
+        is_admin = resp_json.get("is_admin", False)
 
         # Check if user is a member of any whitelisted organizations.
         # This check is performed here, as it requires `access_token`.


Test patch:
diff --git a/oauthenticator/tests/test_gitlab.py b/oauthenticator/tests/test_gitlab.py
index 86d2b45..5163a49 100644
--- a/oauthenticator/tests/test_gitlab.py
+++ b/oauthenticator/tests/test_gitlab.py
@@ -16,11 +16,15 @@ from .mocks import setup_oauth_mock
 
 def user_model(username, id=1, is_admin=False):
     """Return a user model"""
-    return {
+    user = {
         'username': username,
         'id': id,
-        'is_admin': is_admin
     }
+    if is_admin:
+      # Some versions of the API do not return the is_admin property
+        # for non-admin users (See #115).
+        user['is_admin'] = True
+    return user
 
 @fixture
 def gitlab_client(client):
@@ -58,12 +62,10 @@ def test_group_whitelist(gitlab_client):
         'burns': ['blue', 'yellow'],
     })
 
-    def user_model(username, is_admin=False):
-        return {
-            'username': username,
-            'id': list(user_groups.keys()).index(username) + 1,
-            'is_admin': is_admin
-        }
+    def group_user_model(username, is_admin=False):
+        return user_model(username,
+                          list(user_groups.keys()).index(username) + 1,
+                          is_admin)
 
 
     member_regex = re.compile(r'/api/v3/groups/(.*)/members/(.*)')
@@ -116,30 +118,30 @@ def test_group_whitelist(gitlab_client):
 
         authenticator.gitlab_group_whitelist = ['blue']
 
-        handler = client.handler_for_user(user_model('caboose'))
+        handler = client.handler_for_user(group_user_model('caboose'))
         name = yield authenticator.authenticate(handler)
         assert name == 'caboose'
 
-        handler = client.handler_for_user(user_model('burns', is_admin=True))
+        handler = client.handler_for_user(group_user_model('burns', is_admin=True))
         name = yield authenticator.authenticate(handler)
         assert name == 'burns'
 
-        handler = client.handler_for_user(user_model('grif'))
+        handler = client.handler_for_user(group_user_model('grif'))
         name = yield authenticator.authenticate(handler)
         assert name is None
 
-        handler = client.handler_for_user(user_model('simmons', is_admin=True))
+        handler = client.handler_for_user(group_user_model('simmons', is_admin=True))
         name = yield authenticator.authenticate(handler)
         assert name is None
 
         # reverse it, just to be safe
         authenticator.gitlab_group_whitelist = ['red']
 
-        handler = client.handler_for_user(user_model('caboose'))
+        handler = client.handler_for_user(group_user_model('caboose'))
         name = yield authenticator.authenticate(handler)
         assert name is None
 
-        handler = client.handler_for_user(user_model('grif'))
+        handler = client.handler_for_user(group_user_model('grif'))
         name = yield authenticator.authenticate(handler)
         assert name == 'grif'