-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make APIv3 permission classes modular #5858
Conversation
d9f08a9
to
6302256
Compare
This is needed to render the Form for POST when using BrowsableAPIRenderer and hitting /projects/
ed42f57
to
064889a
Compare
readthedocs/api/v3/permissions.py
Outdated
return True | ||
|
||
project = view._get_parent_project() | ||
if view.has_admin_permission(request.user, project): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
has_admin_permission
seems poorly named to me. It isn't immediately clear what it means. I realize it means that the user is a maintainer on the project but maybe it should be more explicit. How about is_project_maintainer
or something like that. It does seem to mirror the method name on the ProjectQuerySet
but it is much more clear what that method does in context (Project.objects.for_admin_user
is fairly clear although could be better while view.has_admin_permission
is not).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_project_maintainer
is a good name, but I'm not sure it does fit with corporate (or under an organizational structure) --where only read access is granted.
Even if it doesn't fit 100%, it's still a better name 😄
readthedocs/api/v3/views.py
Outdated
@@ -126,6 +130,20 @@ class ProjectsViewSet(APIv3Settings, NestedViewSetMixin, ProjectQuerySetMixin, | |||
'active_versions.last_build.config', | |||
] | |||
|
|||
def get_serializer_class(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you're doing here, but stylistically, I think it would be better to get rid of this method and make create()
explicitly use a different serializer on incoming POST data.
In all cases, you want the JSON output to use ProjectSerializer
. However, when there's user input like a user creating a project they aren't going to send every last field as some of them are automatic or have reasonable defaults so a different serializer may make sense. Since you're already overriding the output serializer in create()
, I'd rather you just instead overrode the input serializer and used the default output serializer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it looks like this is necessary due to how CreateModelMixin
works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this. Although, since this PR is based on the "Import Project" one, some things changed there and I will re-check this once that one gets merged.
@@ -50,7 +55,7 @@ class APIv3Settings: | |||
# Using only ``TokenAuthentication`` for now, so we can give access to | |||
# specific carefully selected users only | |||
authentication_classes = (TokenAuthentication,) | |||
permission_classes = (PublicDetailPrivateListing,) | |||
permission_classes = (IsAuthenticated & (ListCreateProject | PublicDetailPrivateListing),) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is starting to make me nervous and I don't really understand how ListCreateProject
is that different from PublicDetailPrivateListing
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ouch! I thought that splitting these permissions in different classes would be easier to understand them.
Before this PR, everything was under a black box called PublicDetailPrivateListing
. I decided to split it because it did more than what it name says.
My idea is:
IsAuthenticated
is the first required condition,
then, the user has access to
ListCreateProject
:list
ORcreate
at the/projects/
endpoint (which is kind special)
NOTE:
ListCreateProject
may be moved toProjectsViewSet
since it's the only place where it's needed
OR
- PublicDetailPrivateListing:
detail
on any object orlist
of "private" (personal) objects.
Let me know if this helps to clarify or if you have another idea how to make it better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think overall, I'd feel a lot better if these special cases were not on the base class that every API endpoint extends from. The project endpoint is special (although with all the extra complexity, I'm starting to doubt that it should be) and these changes should be localized to there!
c44f039
to
b29b3b5
Compare
@humitos there are merge conflicts to be resolved before review |
@stsewd I fixed the conflicts and change the base to be |
@davidfischer I'd appreciate another round of review here. |
@@ -153,11 +153,6 @@ def test_import_project(self): | |||
response_json['created'] = '2019-04-29T10:00:00Z' | |||
response_json['modified'] = '2019-04-29T12:00:00Z' | |||
|
|||
self.assertDictEqual( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I we remove this, the previous couple lines don't do anything, right?
@@ -50,7 +55,7 @@ class APIv3Settings: | |||
# Using only ``TokenAuthentication`` for now, so we can give access to | |||
# specific carefully selected users only | |||
authentication_classes = (TokenAuthentication,) | |||
permission_classes = (PublicDetailPrivateListing,) | |||
permission_classes = (IsAuthenticated & (ListCreateProject | PublicDetailPrivateListing),) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think overall, I'd feel a lot better if these special cases were not on the base class that every API endpoint extends from. The project endpoint is special (although with all the extra complexity, I'm starting to doubt that it should be) and these changes should be localized to there!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
I believe this is still valid. |
I will need to re-visit this PR after #6489 got merged. |
This PR just split our own permission class that handles all the cases into tree where each of them are in charge of an specific thing:
/api/v3/projects/
to list user's projects and also to create a project at the same endpoint by POSTDepends on #5857