Browse Source

Add option to read user admin status from Github

pull/210/head
Clemens Wolff 6 years ago
parent
commit
74630f078a
6 changed files with 263 additions and 0 deletions
  1. 6
      app/app/settings.py
  2. 44
      app/server/social_auth.py
  3. 76
      app/server/tests/cassettes/TestGithubSocialAuth.test_fetch_permissions_is_admin.yaml
  4. 77
      app/server/tests/cassettes/TestGithubSocialAuth.test_fetch_permissions_not_admin.yaml
  5. 57
      app/server/tests/test_social_auth.py
  6. 3
      requirements.txt

6
app/app/settings.py

@ -120,6 +120,11 @@ AUTHENTICATION_BACKENDS = [
SOCIAL_AUTH_GITHUB_KEY = env('OAUTH_GITHUB_KEY', None)
SOCIAL_AUTH_GITHUB_SECRET = env('OAUTH_GITHUB_SECRET', None)
GITHUB_ADMIN_ORG_NAME = env('GITHUB_ADMIN_ORG_NAME', None)
GITHUB_ADMIN_TEAM_NAME = env('GITHUB_ADMIN_TEAM_NAME', None)
if GITHUB_ADMIN_ORG_NAME and GITHUB_ADMIN_TEAM_NAME:
SOCIAL_AUTH_GITHUB_SCOPE = ['read:org']
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = env('OAUTH_AAD_KEY', None)
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = env('OAUTH_AAD_SECRET', None)
@ -135,6 +140,7 @@ SOCIAL_AUTH_PIPELINE = [
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
'server.social_auth.fetch_github_permissions',
]
# Database

44
app/server/social_auth.py

@ -0,0 +1,44 @@
import requests
from django.conf import settings
from social_core.backends.github import GithubOAuth2
# noinspection PyUnusedLocal
def fetch_github_permissions(strategy, details, user=None, is_new=False, *args, **kwargs):
org_name = getattr(settings, 'GITHUB_ADMIN_ORG_NAME', '')
team_name = getattr(settings, 'GITHUB_ADMIN_TEAM_NAME', '')
if not user or not isinstance(kwargs['backend'], GithubOAuth2) or not org_name or not team_name:
return
response = requests.post(
url='https://api.github.com/graphql',
headers={
'Authorization': 'Bearer {}'.format(kwargs['response']['access_token']),
},
json={
'query': '''
query($userName: String!, $orgName: String!, $teamName: String!) {
organization(login: $orgName) {
teams(query: $teamName, userLogins: [$userName], first: 1) {
nodes {
name
}
}
}
}
''',
'variables': {
'userName': details['username'],
'orgName': org_name,
'teamName': team_name,
}
}
)
response.raise_for_status()
response = response.json()
is_superuser = {'name': team_name} in response['data']['organization']['teams']['nodes']
if user.is_superuser != is_superuser:
user.is_superuser = is_superuser
user.save()

76
app/server/tests/cassettes/TestGithubSocialAuth.test_fetch_permissions_is_admin.yaml

@ -0,0 +1,76 @@
interactions:
- request:
body: '{"query": "\n query($userName: String!, $orgName: String!,
$teamName: String!) {\n organization(login: $orgName) {\n teams(query:
$teamName, userLogins: [$userName], first: 1) {\n nodes
{\n name\n }\n }\n }\n }\n ",
"variables": {"userName": "c-w", "orgName": "CatalystCode", "teamName": "doccano-dev"}}'
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
Content-Length:
- '513'
Content-Type:
- application/json
User-Agent:
- python-requests/2.21.0
method: POST
uri: https://api.github.com/graphql
response:
body:
string: '{"data":{"organization":{"teams":{"nodes":[{"name":"doccano-dev"}]}}}}'
headers:
Access-Control-Allow-Origin:
- '*'
Access-Control-Expose-Headers:
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
X-GitHub-Media-Type
Cache-Control:
- no-cache
Content-Security-Policy:
- default-src 'none'
Content-Type:
- application/json; charset=utf-8
Date:
- Mon, 20 May 2019 17:38:20 GMT
Referrer-Policy:
- origin-when-cross-origin, strict-origin-when-cross-origin
Server:
- GitHub.com
Status:
- 200 OK
Strict-Transport-Security:
- max-age=31536000; includeSubdomains; preload
Transfer-Encoding:
- chunked
X-Accepted-OAuth-Scopes:
- repo
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- deny
X-GitHub-Media-Type:
- github.v4; format=json
X-GitHub-Request-Id:
- E979:03BD:225D930:49FB694:5CE2E60C
X-OAuth-Scopes:
- read:org
X-RateLimit-Limit:
- '5000'
X-RateLimit-Remaining:
- '4955'
X-RateLimit-Reset:
- '1558377500'
X-XSS-Protection:
- 1; mode=block
content-length:
- '70'
status:
code: 200
message: OK
version: 1

77
app/server/tests/cassettes/TestGithubSocialAuth.test_fetch_permissions_not_admin.yaml

@ -0,0 +1,77 @@
interactions:
- request:
body: '{"query": "\n query($userName: String!, $orgName: String!,
$teamName: String!) {\n organization(login: $orgName) {\n teams(query:
$teamName, userLogins: [$userName], first: 1) {\n nodes
{\n name\n }\n }\n }\n }\n ",
"variables": {"userName": "hirosan", "orgName": "CatalystCode", "teamName":
"doccano-dev"}}'
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
Content-Length:
- '517'
Content-Type:
- application/json
User-Agent:
- python-requests/2.21.0
method: POST
uri: https://api.github.com/graphql
response:
body:
string: '{"data":{"organization":{"teams":{"nodes":[]}}}}'
headers:
Access-Control-Allow-Origin:
- '*'
Access-Control-Expose-Headers:
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
X-GitHub-Media-Type
Cache-Control:
- no-cache
Content-Security-Policy:
- default-src 'none'
Content-Type:
- application/json; charset=utf-8
Date:
- Mon, 20 May 2019 17:38:20 GMT
Referrer-Policy:
- origin-when-cross-origin, strict-origin-when-cross-origin
Server:
- GitHub.com
Status:
- 200 OK
Strict-Transport-Security:
- max-age=31536000; includeSubdomains; preload
Transfer-Encoding:
- chunked
X-Accepted-OAuth-Scopes:
- repo
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- deny
X-GitHub-Media-Type:
- github.v4; format=json
X-GitHub-Request-Id:
- E97B:0EFE:220AB47:4963FE2:5CE2E60C
X-OAuth-Scopes:
- read:org
X-RateLimit-Limit:
- '5000'
X-RateLimit-Remaining:
- '4954'
X-RateLimit-Reset:
- '1558377500'
X-XSS-Protection:
- 1; mode=block
content-length:
- '48'
status:
code: 200
message: OK
version: 1

57
app/server/tests/test_social_auth.py

@ -0,0 +1,57 @@
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from social_core.backends.github import GithubOAuth2
from vcr_unittest import VCRMixin
from .. import social_auth
User = get_user_model()
class VCRTestCase(VCRMixin, TestCase):
@property
def access_token(self):
raise NotImplementedError()
def _get_vcr(self, **kwargs):
kwargs['decode_compressed_response'] = True
kwargs['record_mode'] = 'none' if self.access_token == 'censored' else 'all'
return super()._get_vcr(**kwargs)
def _get_vcr_kwargs(self, **kwargs):
kwargs['filter_headers'] = ['Authorization']
return super()._get_vcr_kwargs(**kwargs)
@override_settings(GITHUB_ADMIN_ORG_NAME='CatalystCode')
@override_settings(GITHUB_ADMIN_TEAM_NAME='doccano-dev')
class TestGithubSocialAuth(VCRTestCase):
strategy = None
backend = GithubOAuth2(strategy=strategy)
access_token = 'censored'
def test_fetch_permissions_is_admin(self):
user = User()
social_auth.fetch_github_permissions(
strategy=self.strategy,
details={'username': 'c-w'},
user=user,
backend=self.backend,
response={'access_token': self.access_token},
)
self.assertTrue(user.is_superuser)
def test_fetch_permissions_not_admin(self):
user = User()
social_auth.fetch_github_permissions(
strategy=self.strategy,
details={'username': 'hirosan'},
user=user,
backend=self.backend,
response={'access_token': self.access_token},
)
self.assertFalse(user.is_superuser)

3
requirements.txt

@ -21,6 +21,7 @@ model-mommy==1.6.0
psycopg2-binary==2.7.7
python-dateutil==2.7.3
pytz==2018.4
requests==2.21.0
six==1.11.0
seqeval==0.0.6
social-auth-app-django==3.1.0
@ -28,4 +29,6 @@ social-auth-core[azuread]==3.0.0
text-unidecode==1.2
tornado==5.0.2
unittest-xml-reporting==2.5.1
vcrpy==2.0.1
vcrpy-unittest==0.1.7
whitenoise[brotli]==4.1.2
Loading…
Cancel
Save