mirror of https://github.com/doccano/doccano.git
Browse Source
Merge pull request #210 from CatalystCode/enhancement/social-admin-info
Merge pull request #210 from CatalystCode/enhancement/social-admin-info
Enhancement/Read admin info from third-party authpull/232/head
Hiroki Nakayama
5 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 450 additions and 1 deletions
Split View
Diff Options
-
26app/app/settings.py
-
70app/server/social_auth.py
-
52app/server/tests/cassettes/TestAzureADTenantSocialAuth.test_fetch_permissions_is_admin.yaml
-
52app/server/tests/cassettes/TestAzureADTenantSocialAuth.test_fetch_permissions_not_admin.yaml
-
76app/server/tests/cassettes/TestGithubSocialAuth.test_fetch_permissions_is_admin.yaml
-
77app/server/tests/cassettes/TestGithubSocialAuth.test_fetch_permissions_not_admin.yaml
-
95app/server/tests/test_social_auth.py
-
3requirements.txt
@ -0,0 +1,70 @@ |
|||
import requests |
|||
from django.conf import settings |
|||
from social_core.backends.azuread_tenant import AzureADTenantOAuth2 |
|||
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() |
|||
|
|||
|
|||
# noinspection PyUnusedLocal |
|||
def fetch_azuread_permissions(strategy, details, user=None, is_new=False, *args, **kwargs): |
|||
group_id = getattr(settings, 'AZUREAD_ADMIN_GROUP_ID', '') |
|||
if not user or not isinstance(kwargs['backend'], AzureADTenantOAuth2) or not group_id: |
|||
return |
|||
|
|||
response = requests.post( |
|||
url='https://graph.microsoft.com/v1.0/me/checkMemberGroups', |
|||
headers={ |
|||
'Authorization': 'Bearer {}'.format(kwargs['response']['access_token']), |
|||
}, |
|||
json={ |
|||
'groupIds': [group_id] |
|||
} |
|||
) |
|||
response.raise_for_status() |
|||
response = response.json() |
|||
|
|||
is_superuser = group_id in response['value'] |
|||
|
|||
if user.is_superuser != is_superuser: |
|||
user.is_superuser = is_superuser |
|||
user.save() |
@ -0,0 +1,52 @@ |
|||
interactions: |
|||
- request: |
|||
body: '{"groupIds": ["dddddddd-dddd-dddd-dddd-dddddddddddd"]}' |
|||
headers: |
|||
Accept: |
|||
- '*/*' |
|||
Accept-Encoding: |
|||
- gzip, deflate |
|||
Connection: |
|||
- keep-alive |
|||
Content-Length: |
|||
- '54' |
|||
Content-Type: |
|||
- application/json |
|||
User-Agent: |
|||
- python-requests/2.21.0 |
|||
method: POST |
|||
uri: https://graph.microsoft.com/v1.0/me/checkMemberGroups |
|||
response: |
|||
body: |
|||
string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(Edm.String)","value":["dddddddd-dddd-dddd-dddd-dddddddddddd"]}' |
|||
headers: |
|||
Cache-Control: |
|||
- private |
|||
Content-Type: |
|||
- application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8 |
|||
Date: |
|||
- Mon, 20 May 2019 19:34:27 GMT |
|||
Duration: |
|||
- '89.2611' |
|||
Location: |
|||
- https://graph.microsoft.com |
|||
OData-Version: |
|||
- '4.0' |
|||
Strict-Transport-Security: |
|||
- max-age=31536000 |
|||
Transfer-Encoding: |
|||
- chunked |
|||
Vary: |
|||
- Accept-Encoding |
|||
client-request-id: |
|||
- 78c55087-ba07-4e80-8498-8b23b1901356 |
|||
content-length: |
|||
- '135' |
|||
request-id: |
|||
- 78c55087-ba07-4e80-8498-8b23b1901356 |
|||
x-ms-ags-diagnostic: |
|||
- '{"ServerInfo":{"DataCenter":"East US","Slice":"SliceC","Ring":"2","ScaleUnit":"000","RoleInstance":"AGSFE_IN_51","ADSiteName":"EUS"}}' |
|||
status: |
|||
code: 200 |
|||
message: OK |
|||
version: 1 |
@ -0,0 +1,52 @@ |
|||
interactions: |
|||
- request: |
|||
body: '{"groupIds": ["eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee"]}' |
|||
headers: |
|||
Accept: |
|||
- '*/*' |
|||
Accept-Encoding: |
|||
- gzip, deflate |
|||
Connection: |
|||
- keep-alive |
|||
Content-Length: |
|||
- '54' |
|||
Content-Type: |
|||
- application/json |
|||
User-Agent: |
|||
- python-requests/2.21.0 |
|||
method: POST |
|||
uri: https://graph.microsoft.com/v1.0/me/checkMemberGroups |
|||
response: |
|||
body: |
|||
string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#Collection(Edm.String)","value":[]}' |
|||
headers: |
|||
Cache-Control: |
|||
- private |
|||
Content-Type: |
|||
- application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8 |
|||
Date: |
|||
- Mon, 20 May 2019 19:34:52 GMT |
|||
Duration: |
|||
- '84.3986' |
|||
Location: |
|||
- https://graph.microsoft.com |
|||
OData-Version: |
|||
- '4.0' |
|||
Strict-Transport-Security: |
|||
- max-age=31536000 |
|||
Transfer-Encoding: |
|||
- chunked |
|||
Vary: |
|||
- Accept-Encoding |
|||
client-request-id: |
|||
- 69bf4728-ae4e-47ef-afd0-7d2c31129b83 |
|||
content-length: |
|||
- '97' |
|||
request-id: |
|||
- 69bf4728-ae4e-47ef-afd0-7d2c31129b83 |
|||
x-ms-ags-diagnostic: |
|||
- '{"ServerInfo":{"DataCenter":"East US","Slice":"SliceC","Ring":"2","ScaleUnit":"000","RoleInstance":"AGSFE_IN_5","ADSiteName":"EUS"}}' |
|||
status: |
|||
code: 200 |
|||
message: OK |
|||
version: 1 |
@ -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 |
@ -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 |
@ -0,0 +1,95 @@ |
|||
from django.contrib.auth import get_user_model |
|||
from django.test import TestCase, override_settings |
|||
from social_core.backends.azuread_tenant import AzureADTenantOAuth2 |
|||
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) |
|||
|
|||
|
|||
@override_settings(SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY='aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa') |
|||
@override_settings(SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET='bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=') |
|||
@override_settings(SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT='cccccccc-cccc-cccc-cccc-cccccccccccc') |
|||
class TestAzureADTenantSocialAuth(VCRTestCase): |
|||
strategy = None |
|||
backend = AzureADTenantOAuth2(strategy=strategy) |
|||
access_token = 'censored' |
|||
|
|||
@override_settings(AZUREAD_ADMIN_GROUP_ID='dddddddd-dddd-dddd-dddd-dddddddddddd') |
|||
def test_fetch_permissions_is_admin(self): |
|||
user = User() |
|||
|
|||
social_auth.fetch_azuread_permissions( |
|||
strategy=self.strategy, |
|||
details={}, |
|||
user=user, |
|||
backend=self.backend, |
|||
response={'access_token': self.access_token}, |
|||
) |
|||
|
|||
self.assertTrue(user.is_superuser) |
|||
|
|||
@override_settings(AZUREAD_ADMIN_GROUP_ID='eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee') |
|||
def test_fetch_permissions_not_admin(self): |
|||
user = User() |
|||
|
|||
social_auth.fetch_azuread_permissions( |
|||
strategy=self.strategy, |
|||
details={}, |
|||
user=user, |
|||
backend=self.backend, |
|||
response={'access_token': self.access_token}, |
|||
) |
|||
|
|||
self.assertFalse(user.is_superuser) |
Write
Preview
Loading…
Cancel
Save