Skip to content

Commit b204787

Browse files
authored
Improve SSO querysets (#12447)
We were iterating over several objects like teams and user owners in order to get projects, and also checking if the organization attached to that object didn't have SSO with VCS enabled. So instead of doing that I'm just doing one query, which ignores all objects from organizations that have SSO with VCS enabled. Also, instead of calling _get_projects_for_sso_user twice, I adapted that method so it can fetch projects from where the user is admin or member in just one call (similar to what the other methods do). All these changes reduce the number of queries used in the dashboard on .com
1 parent 5dc774d commit b204787

File tree

1 file changed

+14
-32
lines changed

1 file changed

+14
-32
lines changed

readthedocs/core/permissions.py

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -45,44 +45,26 @@ def projects(cls, user, admin=False, member=False):
4545
# when we aren't using organizations.
4646
return user.projects.all()
4747

48-
# Internal cache to avoid hitting the database/cache multiple times.
49-
_organizations_with_allauth_sso = {}
50-
51-
def _has_sso_with_allauth_enabled(org):
52-
if org.pk not in _organizations_with_allauth_sso:
53-
_organizations_with_allauth_sso[org.pk] = cls.has_sso_enabled(
54-
org,
55-
SSOIntegration.PROVIDER_ALLAUTH,
56-
)
57-
return _organizations_with_allauth_sso[org.pk]
58-
59-
# Projects from teams
6048
if admin or member:
49+
# Projects from VCS SSO.
50+
projects |= cls._get_projects_for_sso_user(user, admin=admin, member=member)
51+
52+
# Projects from teams that don't have VCS SSO enabled.
6153
filter = Q()
6254
if admin:
63-
filter |= Q(access=ADMIN_ACCESS)
55+
filter |= Q(teams__access=ADMIN_ACCESS)
6456
if member:
65-
filter |= Q(access=READ_ONLY_ACCESS)
66-
67-
teams = user.teams.filter(filter).select_related(
68-
"organization", "organization__ssointegration"
57+
filter |= Q(teams__access=READ_ONLY_ACCESS)
58+
projects |= Project.objects.filter(filter, teams__members=user).exclude(
59+
organizations__ssointegration__provider=SSOIntegration.PROVIDER_ALLAUTH,
6960
)
70-
for team in teams:
71-
if not _has_sso_with_allauth_enabled(team.organization):
72-
projects |= team.projects.all()
7361

62+
# Projects from organizations that don't have VCS SSO enabled,
63+
# where the user is an owner.
7464
if admin:
75-
# Org Admin
76-
for org in user.owner_organizations.all():
77-
if not _has_sso_with_allauth_enabled(org):
78-
# Do not grant admin access on projects for owners if the
79-
# organization has SSO enabled with Authorization on the provider.
80-
projects |= org.projects.all()
81-
82-
projects |= cls._get_projects_for_sso_user(user, admin=True)
83-
84-
if member:
85-
projects |= cls._get_projects_for_sso_user(user, admin=False)
65+
projects |= Project.objects.filter(organizations__owners=user).exclude(
66+
organizations__ssointegration__provider=SSOIntegration.PROVIDER_ALLAUTH,
67+
)
8668

8769
return projects
8870

@@ -107,7 +89,7 @@ def has_sso_enabled(cls, obj, provider=None):
10789
return False
10890

10991
@classmethod
110-
def _get_projects_for_sso_user(cls, user, admin=False):
92+
def _get_projects_for_sso_user(cls, user, admin=False, member=False):
11193
from readthedocs.projects.models import Project
11294

11395
return Project.objects.none()

0 commit comments

Comments
 (0)