Skip to content

Commit 041edb3

Browse files
authored
Merge pull request #7183 from readthedocs/humitos/db-user-github-sso
2 parents 1699307 + 8c354f6 commit 041edb3

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

readthedocs/core/permissions.py

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,47 @@
11
# -*- coding: utf-8 -*-
22

33
"""Objects for User permission checks."""
4+
from django.contrib.auth.models import User
5+
from django.db.models import Q
46

57
from readthedocs.core.utils.extend import SettingsOverrideObject
68

79

810
class AdminPermissionBase:
911

1012
@classmethod
11-
def is_admin(cls, user, project):
13+
def admins(cls, obj):
14+
from readthedocs.projects.models import Project
15+
from readthedocs.organizations.models import Organization
16+
17+
if isinstance(obj, Project):
18+
return obj.users.all()
19+
20+
if isinstance(obj, Organization):
21+
return obj.owners.all()
22+
23+
@classmethod
24+
def members(cls, obj):
25+
from readthedocs.projects.models import Project
26+
from readthedocs.organizations.models import Organization
27+
28+
if isinstance(obj, Project):
29+
return obj.users.all()
30+
31+
if isinstance(obj, Organization):
32+
return User.objects.filter(
33+
Q(teams__organization=obj) | Q(owner_organizations=obj),
34+
).distinct()
35+
36+
@classmethod
37+
def is_admin(cls, user, obj):
1238
# This explicitly uses "user in project.users.all" so that
1339
# users on projects can be cached using prefetch_related or prefetch_related_objects
14-
return user in project.users.all() or user.is_superuser
40+
return user in cls.admins(obj) or user.is_superuser
1541

1642
@classmethod
1743
def is_member(cls, user, obj):
18-
return user in obj.users.all() or user.is_superuser
44+
return user in cls.members(obj) or user.is_superuser
1945

2046

2147
class AdminPermission(SettingsOverrideObject):

readthedocs/oauth/services/github.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from allauth.socialaccount.models import SocialToken
88
from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter
9+
10+
from django.db.models import Q
911
from django.conf import settings
1012
from django.urls import reverse
1113
from requests.exceptions import RequestException
@@ -35,15 +37,23 @@ class GitHubService(Service):
3537

3638
def sync(self):
3739
"""Sync repositories and organizations."""
38-
self.sync_repositories()
39-
self.sync_organizations()
40+
repos = self.sync_repositories()
41+
organization_repos = self.sync_organizations()
42+
43+
# Delete RemoteRepository where the user doesn't have access anymore
44+
# (skip RemoteRepository tied to a Project on this user)
45+
full_names = {repo.get('full_name') for repo in repos + organization_repos}
46+
self.user.oauth_repositories.exclude(
47+
Q(full_name__in=full_names) | Q(project__isnull=False)
48+
).delete()
4049

4150
def sync_repositories(self):
4251
"""Sync repositories from GitHub API."""
4352
repos = self.paginate('https://api.github.com/user/repos?per_page=100')
4453
try:
4554
for repo in repos:
4655
self.create_repository(repo)
56+
return repos
4757
except (TypeError, ValueError):
4858
log.warning('Error syncing GitHub repositories')
4959
raise SyncServiceError(
@@ -65,6 +75,7 @@ def sync_organizations(self):
6575
)
6676
for repo in org_repos:
6777
self.create_repository(repo, organization=org_obj)
78+
return org_repos
6879
except (TypeError, ValueError):
6980
log.warning('Error syncing GitHub organizations')
7081
raise SyncServiceError(

readthedocs/organizations/models.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
from autoslug import AutoSlugField
44
from django.contrib.auth.models import User
55
from django.db import models
6-
from django.db.models import Q
76
from django.urls import reverse
87
from django.utils.crypto import salted_hmac
98
from django.utils.translation import ugettext_lazy as _
109

1110
from readthedocs.core.utils import slugify
11+
from readthedocs.core.permissions import AdminPermission
1212

1313
from . import constants
1414
from .managers import TeamManager, TeamMemberManager
@@ -93,14 +93,11 @@ def get_absolute_url(self):
9393

9494
@property
9595
def users(self):
96-
return (self.members.all() | self.owners.all().distinct()).distinct()
96+
return AdminPermission.members(self)
9797

9898
@property
9999
def members(self):
100-
"""Return members as an aggregate over all organization teams."""
101-
return User.objects.filter(
102-
Q(teams__organization=self) | Q(owner_organizations=self),
103-
).distinct()
100+
return AdminPermission.members(self)
104101

105102
def save(self, *args, **kwargs): # pylint: disable=arguments-differ
106103
if not self.slug:

0 commit comments

Comments
 (0)