Closed lukik closed 6 years ago
Hello!
The public
parameter is used to hide APIs from the generated specification according to the permissions of the user who views the API documentation page. With public=True
, anyone who has access to view the swagger page can see ALL endpoints in the api, while with False
, they would only see the ones they can actually call according to their current credentials.
If you want to restrict who has access to the page itself, that would be achived, as with any regular view, via permission_classes
. The get_schema_view
method also provides it as a convenience argument, so you could achieve what you want simply by replacing permission_classes=(permissions.AllowAny,),
with, say, permission_classes=(permissions.IsAuthenticated,),
, or whatever floats your boat.
The main thing to take away is that the permission_classes
on the swagger view are unrelated to the public
argument to the spec generator - one controls who can VIEW the spec, while the other controls what endpoints are included in it.
Just now read the stack overflow question more closely - the error you are getting happens because you try to access the endpoint without authentication. I see that the error handling is not exactly on point for this case.
You could wrap the views in login_required: https://docs.djangoproject.com/en/2.0/topics/class-based-views/intro/#decorating-class-based-views
from django.contrib.auth.decorators import login_required,
.. login_required(schema_view.with_ui('swagger', cache_timeout=None)) ... # same for the other two
Great explanation, i stumbled upon this too. 👍✨
@axnsan12 thanks for the guide. However, I tried the following...
from django.contrib.auth.decorators import login_required
schema_view = get_schema_view(
openapi.Info(
title="Swagger Doc",
default_version='v1',
description="Test description",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="contact@snippets.local"),
license=openapi.License(name="BSD License"),
),
# validators=['flex', 'ssv'],
permission_classes=(permissions.IsAuthenticated,), # If I change the permission it throws an exception
public=False,
patterns=public_apis,
)
And URLs, I added the login_required...
url(r'^docs(?P<format>.json|.yaml)$', login_required(schema_view.without_ui(cache_timeout=None)), name='schema-json'),
url(r'^docs/$', login_required(schema_view.with_ui('swagger', cache_timeout=None)), name='schema-swagger-ui'),
url(r'^redoc/$', login_required(schema_view.with_ui('redoc', cache_timeout=None)), name='schema-redoc'),
permissions.AllowAny:
With permissions.AllowAny
and public=false
, the APIs that require to be hidden before someone has authenticated do not show but the Swagger view loads. Just that it does not have any API on it.
Any other Permission Type:
With permissions.IsAuthenticated
or -any other permission for that matter- , the swagger view fails with the error below.
Internal Server Error: /swagger/
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 217, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 215, in _get_response
response = response.render()
File "/usr/local/lib/python3.6/site-packages/django/template/response.py", line 107, in render
self.content = self.rendered_content
File "/usr/local/lib/python3.6/site-packages/rest_framework/response.py", line 72, in rendered_content
ret = renderer.render(self.data, accepted_media_type, context)
File "/usr/local/lib/python3.6/site-packages/drf_yasg/renderers.py", line 54, in render
self.set_context(renderer_context, swagger)
File "/usr/local/lib/python3.6/site-packages/drf_yasg/renderers.py", line 62, in set_context
renderer_context['title'] = swagger.info.title
AttributeError: 'dict' object has no attribute 'info'
Internal Server Error: /swagger/
So am not sure what am doing wrong..
@chgad any tips?
So, the 'dict' object has no attribute 'info'
error you are getting happens because the renderer receives a plain dict and fails to check for non-200 staus code (here), so any outcome SchemaView#get except success would trigger the error.
My assumption was that your error happened because of lacking authentication, but it might also happen for other reasons, running with a debugger might help - the error details probably arrive as the swagger
parameter of render.
I'll try to fix the error handling this weekend to avoid burying the error like this.
@axnsan12 I also got the same error. In my case it's permissions.IsAdminUser
.
Analysing it with my Debugger lead to the following statement : the local variable swagger is dictionary which only contains {'detail'."Authentication credentials were not provided."}
.
After seeing this i first loggedin the admin panel and the tried again. Aaaaaaaaaaaaaaaaaaaaaaand it worked, so my first guess is, that the swagger Renderer does not comunicate with the Authenticationbackend correctly.
I'd like to help further but i don't see where the swagger
variable enters and where it's origin is.
@chgad it enters via the response object returned by the view, as is normal for django rest framework renderers (http://www.django-rest-framework.org/api-guide/renderers/).
The request and response are passed in renderer_context, and the problem is that swagger
is expected to be a Swagger object, which is not the case for a non-200 response. I don't yet know exactly what would be the correct treatment, should probably somehow render an error page.
Finally worked.
Turned out the issue was that I had only rest_framework_jwt.authentication.JSONWebTokenAuthentication
in REST_FRAMEWORK's
DEFAULT_AUTHENTICATION_CLASSES
. So I guess the request
object sent after logging in via the Django Admin login view did not have a user
so all permission classes kept failing and it would then lead to the above error?
So to solve it I added 'rest_framework.authentication.SessionAuthentication',
so the login works okay and drf-yasg shows all its glory.
@axnsan12 good job on this. Really solved a big documentation gap.
If i add rest_framework.authentication.SessionAuthentication, my apis start working without auth token in the header
@ihd2911 same here. Did you find a solution?
No, it didn't. I had to change package
@lukik
Finally worked.
Turned out the issue was that I had only
rest_framework_jwt.authentication.JSONWebTokenAuthentication
inREST_FRAMEWORK's
DEFAULT_AUTHENTICATION_CLASSES
. So I guess therequest
object sent after logging in via the Django Admin login view did not have auser
so all permission classes kept failing and it would then lead to the above error?So to solve it I added
'rest_framework.authentication.SessionAuthentication',
so the login works okay and drf-yasg shows all its glory.@axnsan12 good job on this. Really solved a big documentation gap.
Even after adding SessionAuthentication to DEFAULT_AUTHENTICATION_CLASSES, and even above TokenAuthentication, using any sort of permission_class with DRF_yasg tends to think it is an AnonymousUser trying to view the API. This happens even when I'm 100% logged in through Django admin. Any clue how I could fix this? I've tested it with my own custom permission and printing 'request.user', - it still shows as an AnonymousUser.
Finally worked.
Turned out the issue was that I had only
rest_framework_jwt.authentication.JSONWebTokenAuthentication
inREST_FRAMEWORK's
DEFAULT_AUTHENTICATION_CLASSES
. So I guess therequest
object sent after logging in via the Django Admin login view did not have auser
so all permission classes kept failing and it would then lead to the above error?So to solve it I added
'rest_framework.authentication.SessionAuthentication',
so the login works okay and drf-yasg shows all its glory.@axnsan12 good job on this. Really solved a big documentation gap.
You should not add SessionAuthentication
directly in the REST_FRAMEWORK settings because it means that now even Session auth in Django admin can be considered as working authentication method by your API endpoints. Not sure it's what you want. Instead, the get_schema_view
also has authentication_classes
as kwargs. Pass SessionAuthentication
as value for this key. Like that this authentication will be accepted to login in the api documentation, but not for your API endpoints.
Example:
schema_view = get_schema_view(
openapi.Info(
title="...",
default_version='...',
description="...",
terms_of_service="",
contact=openapi.Contact(email="...@...),
license=openapi.License(name="..."),
),
public=False,
authentication_classes=(SessionAuthentication,),
permission_classes=(permissions.IsAdminUser,),
)
I've setup DRF-YASG but it seems not able to hide APIs that require Authentication.
Below is the configuration.
With the above configuration, the
sample-end-point
does not appear on the SwaggerUi. It only shows theAuthorize
Button and text that saysNo operations defined in spec!
. But if I change thepublic=False
topublic=True
setting, thesample-end-point
appears. But that is not what I want as this URL should be accessible only after one logs in.Is there something am not understanding or a setting am missing?
My Swagger settings are as below:
I'd posted the issue on Stack Overflow