Skip to content

Commit bb2a1b9

Browse files
committed
Remove privacy app
The privacy app was a strange mixture of various application models managers, querysets, and syncers. Instead, logic is moved to where we should be using it, inside the other applications
1 parent d3c1e8a commit bb2a1b9

27 files changed

+479
-434
lines changed

readthedocs/builds/managers.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Build and Version class model Managers"""
2+
3+
from __future__ import absolute_import
4+
5+
from django.db import models
6+
7+
from .constants import (BRANCH, TAG, LATEST, LATEST_VERBOSE_NAME, STABLE,
8+
STABLE_VERBOSE_NAME)
9+
from .querysets import VersionQuerySet
10+
from readthedocs.core.utils.extend import (SettingsOverrideObject,
11+
get_override_class)
12+
13+
14+
__all__ = ['VersionManager']
15+
16+
17+
class VersionManagerBase(models.Manager):
18+
19+
"""Version manager for manager only queries
20+
21+
For queries not suitable for the :py:cls:`VersionQuerySet`, such as create
22+
queries.
23+
"""
24+
25+
@classmethod
26+
def from_queryset(cls, queryset_class, class_name=None):
27+
# This is overridden because :py:meth:`models.Manager.from_queryset`
28+
# uses `inspect` to retrieve the class methods, and the proxy class has
29+
# no direct members.
30+
queryset_class = get_override_class(
31+
VersionQuerySet,
32+
VersionQuerySet._default_class # pylint: disable=protected-access
33+
)
34+
return super(VersionManagerBase, cls).from_queryset(queryset_class, class_name)
35+
36+
def create_stable(self, **kwargs):
37+
defaults = {
38+
'slug': STABLE,
39+
'verbose_name': STABLE_VERBOSE_NAME,
40+
'machine': True,
41+
'active': True,
42+
'identifier': STABLE,
43+
'type': TAG,
44+
}
45+
defaults.update(kwargs)
46+
return self.create(**defaults)
47+
48+
def create_latest(self, **kwargs):
49+
defaults = {
50+
'slug': LATEST,
51+
'verbose_name': LATEST_VERBOSE_NAME,
52+
'machine': True,
53+
'active': True,
54+
'identifier': LATEST,
55+
'type': BRANCH,
56+
}
57+
defaults.update(kwargs)
58+
return self.create(**defaults)
59+
60+
61+
class VersionManager(SettingsOverrideObject):
62+
_default_class = VersionManagerBase
63+
_override_setting = 'VERSION_MANAGER'

readthedocs/builds/models.py

+11-12
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,32 @@
11
"""Models for the builds app."""
22

33
from __future__ import absolute_import
4-
from builtins import object
4+
55
import logging
6-
import re
76
import os.path
7+
import re
88
from shutil import rmtree
99

10-
from django.core.urlresolvers import reverse
10+
from builtins import object
1111
from django.conf import settings
12+
from django.core.urlresolvers import reverse
1213
from django.db import models
1314
from django.utils.encoding import python_2_unicode_compatible
1415
from django.utils.translation import ugettext_lazy as _, ugettext
15-
1616
from guardian.shortcuts import assign
1717
from taggit.managers import TaggableManager
1818

19-
from readthedocs.core.utils import broadcast
20-
from readthedocs.privacy.backend import VersionQuerySet, VersionManager
21-
from readthedocs.privacy.loader import RelatedBuildQuerySet, BuildQuerySet
22-
from readthedocs.projects.models import Project
23-
from readthedocs.projects.constants import (PRIVACY_CHOICES, GITHUB_URL,
24-
GITHUB_REGEXS, BITBUCKET_URL,
25-
BITBUCKET_REGEXS, PRIVATE)
26-
2719
from .constants import (BUILD_STATE, BUILD_TYPES, VERSION_TYPES,
2820
LATEST, NON_REPOSITORY_VERSIONS, STABLE,
2921
BUILD_STATE_FINISHED, BRANCH, TAG)
22+
from .managers import VersionManager
23+
from .querysets import BuildQuerySet, RelatedBuildQuerySet, VersionQuerySet
3024
from .version_slug import VersionSlugField
25+
from readthedocs.core.utils import broadcast
26+
from readthedocs.projects.constants import (PRIVACY_CHOICES, GITHUB_URL,
27+
GITHUB_REGEXS, BITBUCKET_URL,
28+
BITBUCKET_REGEXS, PRIVATE)
29+
from readthedocs.projects.models import Project
3130

3231

3332
DEFAULT_VERSION_PRIVACY_LEVEL = getattr(settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public')

readthedocs/builds/querysets.py

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""Build and Version QuerySet classes"""
2+
3+
from __future__ import absolute_import
4+
5+
from django.db import models
6+
from guardian.shortcuts import get_objects_for_user
7+
8+
from readthedocs.core.utils.extend import SettingsOverrideObject
9+
from readthedocs.projects import constants
10+
11+
12+
__all__ = ['VersionQuerySet', 'BuildQuerySet', 'RelatedBuildQuerySet']
13+
14+
15+
class VersionQuerySetBase(models.QuerySet):
16+
17+
"""Versions take into account their own privacy_level setting."""
18+
19+
use_for_related_fields = True
20+
21+
def _add_user_repos(self, queryset, user):
22+
if user.has_perm('builds.view_version'):
23+
return self.all().distinct()
24+
if user.is_authenticated():
25+
user_queryset = get_objects_for_user(user, 'builds.view_version')
26+
queryset = user_queryset | queryset
27+
return queryset.distinct()
28+
29+
def public(self, user=None, project=None, only_active=True):
30+
queryset = self.filter(privacy_level=constants.PUBLIC)
31+
if user:
32+
queryset = self._add_user_repos(queryset, user)
33+
if project:
34+
queryset = queryset.filter(project=project)
35+
if only_active:
36+
queryset = queryset.filter(active=True)
37+
return queryset
38+
39+
def protected(self, user=None, project=None, only_active=True):
40+
queryset = self.filter(privacy_level__in=[constants.PUBLIC, constants.PROTECTED])
41+
if user:
42+
queryset = self._add_user_repos(queryset, user)
43+
if project:
44+
queryset = queryset.filter(project=project)
45+
if only_active:
46+
queryset = queryset.filter(active=True)
47+
return queryset
48+
49+
def private(self, user=None, project=None, only_active=True):
50+
queryset = self.filter(privacy_level__in=[constants.PRIVATE])
51+
if user:
52+
queryset = self._add_user_repos(queryset, user)
53+
if project:
54+
queryset = queryset.filter(project=project)
55+
if only_active:
56+
queryset = queryset.filter(active=True)
57+
return queryset
58+
59+
def api(self, user=None):
60+
return self.public(user, only_active=False)
61+
62+
def for_project(self, project):
63+
"""Return all versions for a project, including translations"""
64+
return self.filter(
65+
models.Q(project=project) |
66+
models.Q(project__main_language_project=project)
67+
)
68+
69+
70+
class VersionQuerySet(SettingsOverrideObject):
71+
_default_class = VersionQuerySetBase
72+
73+
74+
class BuildQuerySetBase(models.QuerySet):
75+
76+
"""
77+
Build objects that are privacy aware.
78+
79+
i.e. they take into account the privacy of the Version that they relate to.
80+
"""
81+
82+
use_for_related_fields = True
83+
84+
def _add_user_repos(self, queryset, user=None):
85+
if user.has_perm('builds.view_version'):
86+
return self.all().distinct()
87+
if user.is_authenticated():
88+
user_queryset = get_objects_for_user(user, 'builds.view_version')
89+
pks = [p.pk for p in user_queryset]
90+
queryset = self.filter(version__pk__in=pks) | queryset
91+
return queryset.distinct()
92+
93+
def public(self, user=None, project=None):
94+
queryset = self.filter(version__privacy_level=constants.PUBLIC)
95+
if user:
96+
queryset = self._add_user_repos(queryset, user)
97+
if project:
98+
queryset = queryset.filter(project=project)
99+
return queryset
100+
101+
def api(self, user=None):
102+
return self.public(user)
103+
104+
105+
class BuildQuerySet(SettingsOverrideObject):
106+
_default_class = BuildQuerySetBase
107+
_override_setting = 'BUILD_MANAGER'
108+
109+
110+
class RelatedBuildQuerySetBase(models.QuerySet):
111+
112+
"""For models with association to a project through :py:class:`Build`"""
113+
114+
use_for_related_fields = True
115+
116+
def _add_user_repos(self, queryset, user=None):
117+
if user.has_perm('builds.view_version'):
118+
return self.all().distinct()
119+
if user.is_authenticated():
120+
user_queryset = get_objects_for_user(user, 'builds.view_version')
121+
pks = [p.pk for p in user_queryset]
122+
queryset = self.filter(
123+
build__version__pk__in=pks) | queryset
124+
return queryset.distinct()
125+
126+
def public(self, user=None, project=None):
127+
queryset = self.filter(build__version__privacy_level=constants.PUBLIC)
128+
if user:
129+
queryset = self._add_user_repos(queryset, user)
130+
if project:
131+
queryset = queryset.filter(build__project=project)
132+
return queryset
133+
134+
def api(self, user=None):
135+
return self.public(user)
136+
137+
138+
class RelatedBuildQuerySet(SettingsOverrideObject):
139+
_default_class = RelatedBuildQuerySetBase
140+
_override_setting = 'RELATED_BUILD_MANAGER'

readthedocs/privacy/backends/syncers.py renamed to readthedocs/builds/syncers.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
"""Classes allowing copying files around.
1+
"""Classes to copy files between build and web servers
22
33
"Syncers" copy files from the local machine, while "Pullers" copy files to
44
the local machine.
5-
65
"""
6+
77
from __future__ import absolute_import
8-
from builtins import object
8+
99
import getpass
1010
import logging
1111
import os
1212
import shutil
1313

14+
from builtins import object
1415
from django.conf import settings
1516

17+
from readthedocs.core.utils.extend import SettingsOverrideObject
18+
19+
1620
log = logging.getLogger(__name__)
1721

1822

@@ -135,3 +139,8 @@ def copy(cls, path, target, host, is_file=False, **__):
135139
ret = os.system(sync_cmd)
136140
if ret != 0:
137141
log.info("COPY ERROR to app servers.")
142+
143+
144+
class Syncer(SettingsOverrideObject):
145+
_default_class = LocalSyncer
146+
_override_setting = 'FILE_SYNCER'

readthedocs/core/permissions.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""Objects for User permission checks"""
2+
3+
from __future__ import absolute_import
4+
5+
from readthedocs.core.utils.extend import SettingsOverrideObject
6+
7+
8+
class AdminPermissionBase(object):
9+
10+
@classmethod
11+
def is_admin(cls, user, project):
12+
return user in project.users.all()
13+
14+
@classmethod
15+
def is_member(cls, user, obj):
16+
return user in obj.users.all()
17+
18+
19+
class AdminPermission(SettingsOverrideObject):
20+
_default_class = AdminPermissionBase
21+
_override_setting = 'ADMIN_PERMISSION'

readthedocs/privacy/templatetags/privacy_tags.py renamed to readthedocs/core/templatetags/privacy_tags.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""Template tags to query projects by privacy."""
22

33
from __future__ import absolute_import
4-
from django import template
54

6-
from ..loader import AdminPermission
5+
from django import template
76

7+
from readthedocs.core.permissions import AdminPermission
88
from readthedocs.projects.models import Project
99

10+
1011
register = template.Library()
1112

1213

readthedocs/core/views/serve.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
from readthedocs.builds.models import Version
3535
from readthedocs.projects import constants
3636
from readthedocs.projects.models import Project, ProjectRelationship
37-
from readthedocs.core.symlink import PrivateSymlink, PublicSymlink
37+
from readthedocs.core.permissions import AdminPermission
3838
from readthedocs.core.resolver import resolve, resolve_path
39-
from readthedocs.privacy.loader import AdminPermission
39+
from readthedocs.core.symlink import PrivateSymlink, PublicSymlink
4040

4141
import mimetypes
4242
import os

readthedocs/oauth/managers.py

-12
This file was deleted.

readthedocs/oauth/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from readthedocs.projects.constants import REPO_CHOICES
1818
from readthedocs.projects.models import Project
1919

20-
from .managers import RemoteRepositoryQuerySet, RemoteOrganizationQuerySet
20+
from .querysets import RemoteRepositoryQuerySet, RemoteOrganizationQuerySet
2121

2222

2323
DEFAULT_PRIVACY_LEVEL = getattr(settings, 'DEFAULT_PRIVACY_LEVEL', 'public')

readthedocs/oauth/querysets.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Managers for OAuth models"""
2+
3+
from __future__ import absolute_import
4+
5+
from django.db import models
6+
7+
from readthedocs.core.utils.extend import SettingsOverrideObject
8+
9+
10+
class RelatedUserQuerySetBase(models.QuerySet):
11+
12+
"""For models with relations through :py:class:`User`"""
13+
14+
def api(self, user=None):
15+
"""Return objects for user"""
16+
if not user.is_authenticated():
17+
return self.none()
18+
return self.filter(users=user)
19+
20+
21+
class RelatedUserQuerySet(SettingsOverrideObject):
22+
_default_class = RelatedUserQuerySetBase
23+
_override_setting = 'RELATED_USER_MANAGER'
24+
25+
26+
class RemoteRepositoryQuerySet(RelatedUserQuerySet):
27+
pass
28+
29+
30+
class RemoteOrganizationQuerySet(RelatedUserQuerySet):
31+
pass

readthedocs/privacy/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)