how to pass post parameter in Django rest swagger? - django

I have integrated swagger with django rest framework, but the swagger docs does not create a input box to post data for post request.
I am using Function based views with decorator(#api_view)
#api_view(['GET','POST'])
#permission_classes((AllowAny, ))
#renderer_classes([OpenAPIRenderer, SwaggerUIRenderer])
def preferences(request,format=None):
try:
"logic starts here "
in urls.py i have added:
schema_view = get_swagger_view(title='API')
path('', schema_view, name="schema_view"),
in settings.py :
SWAGGER_SETTINGS = {
'USE_SESSION_AUTH': False,
'JSON_EDITOR': True,
'api_version': '0.1',
'enabled_methods': [
'GET',
'POST',
],
'SECURITY_DEFINITIONS': {
"api_key": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
},
},
}
But, when i open the api url i'm getting something like this (in image) where i'm unable to pass the parameters through post
what can be the problem here?is there any other changes to be done ?

When you click on the Try it Out link, you should get an input to fill in the POST data

make sure the class is imported from the generics API, example:
from rest_framework import generics
class Something (generics.GenericAPIView):
serializer_class=SomeSerializer
def post(self,request):
pass

Related

How to add password and username pop up for Django Swagger?

I am using drf-yasg library for the Django Swagger. I need to add the authentication on username and password level. There are three security schemes available in this library "basic", "apiKey" or "oauth2".
Is there any way I can set my credentials for swagger in my django project settings and authenticate the swagger apidocs based on that?
To have the popup for authentication in 'DRF' and also in 'SWAGGER' panel, simply add these lines of code which I arrowed to your settings.py:
'DRF' implementation
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
# the link you can read about
# https://stackoverflow.com/questions/51906745/django-rest-framework-logout-not-working-after-token-authentication
'rest_framework.authentication.BasicAuthentication', # <<--
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
In REST_FRAMEWORK, inside the DEFAULT_AUTHENTICATION_CLASSES, (which is a list) add the
rest_framework.authentication.BasicAuthentication.
It tells the djagno to authenticate using the default authentication that djagno provides.
'SWAGGER' implementation
If you want to use it in 'SWAGGER' as well, do the below:
In SWAGGER_SETTINGS, inside the SECURITY_DEFINITIONS which is a dict, add these lines of code to implement that:
'basic': {
'type': 'basic'
},
Default 'swagger' settings would be like this:
SWAGGER_SETTINGS = {
'DOC_EXPANSION': 'list',
'APIS_SORTER': 'alpha',
'USE_SESSION_AUTH': False,
'SECURITY_DEFINITIONS': {
'Bearer': { # <<-- is for JWT access token
'type': 'apiKey',
'name': 'Authorization',
'in': 'header'
},
'basic': { # <<-- is for djagno authentication
'type': 'basic'
},
},
}
Attention that Bearer is for JWT access token. basic is for djagno authentication.
Thant you for reading!

AllowAny CreateAPIView returns 401 for unauthenticated users

I use DRF with djangorestframework-simplejwt package. In my settings.py:
INSTALLED_APPS = [
...
'rest_framework',
...
]
...
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTTokenUserAuthentication',
),
}
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header',
'description': 'E.g. \'Bearer jwt.token.here\''
}
}
}
And in my apps' views.py:
...
class PublicCreateView(generics.CreateAPIView):
"""
Should be available for unauthenticated users
"""
serializer_class = PublicThingSerializer
def post(self, request, *args, **kwargs):
return Response("It works!", 200)
...
Yet for some reason this view returns 401 response for unauthenticated users. I tried a lot of things, the best I got was noticing that when I remove the REST_FRAMEWORK config from my settings.py completely, the response code changes to 403 forbidden. Any ideas?
As MojixCoder mentinoned in a comment, cleaning the cookies might have solved the issue but this time, after a few hours of debugging it turned out that my problem was actually related to urls.py which declared (simplified):
from . import views
urlpatterns = [
path("something/", views.SomethingViewSet()),
...
path("something/more/", views.PublicCreateView.as_view())
]
And the issue was that while routing a request to url /something/more/ Django actually used the first matching rule (perfectly understandable and expected behavior) which had rest_framework.permissions.IsAuthenticated set in permission_classes. This behavior is described in Django documentation on URL dispatcher under How Django processes a request section, point 3:
Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL, matching against path_info.
Hope it saves someone's time. Since the API only returned a generic 401 answer that was surprisingly hard to figure out.

SAML2 with Django stuck on infinite loop

I am trying to use the djangosaml2 1.1.0 module in order to implement SSO login.
After the successful login, it gets stuck in an infinite loop trying to constantly login again.
I have tried to remove the #login_requried decorator, it works on then.
But, I need the #login_required decorator in order to prevent not logged in users to access specific views.
My believe is that django.contrib.auth.backends.ModelBackend is not properly configured with djangosaml2.backends.Saml2Backend
This is my code:
settings.py
SAML_CONFIG = {
# full path to the xmlsec1 binary programm
'xmlsec_binary': '/usr/bin/xmlsec1',
# your entity id, usually your subdomain plus the url to the metadata view
'entityid': 'http://localhost:8000/saml2/metadata/',
# directory with attribute mapping
'attribute_map_dir': path.join(BASEDIR, 'attribute-maps'),
# this block states what services we provide
'service': {
# we are just a lonely SP
'sp' : {
'name': 'Federated Django sample SP',
'name_id_format': saml2.saml.NAMEID_FORMAT_PERSISTENT,
# For Okta add signed logout requets. Enable this:
# "logout_requests_signed": True,
'endpoints': {
# url and binding to the assetion consumer service view
# do not change the binding or service name
'assertion_consumer_service': [
('http://localhost:8000/tacdb/items/',
saml2.BINDING_HTTP_POST),
],
# url and binding to the single logout service view
# do not change the binding or service name
'single_logout_service': [
# Disable next two lines for HTTP_REDIRECT for IDP's that only support HTTP_POST. Ex. Okta:
('http://localhost:8000/saml2/ls/',
saml2.BINDING_HTTP_REDIRECT),
('http://localhost:8000/saml2/ls/post',
saml2.BINDING_HTTP_POST),
],
},
# Mandates that the identity provider MUST authenticate the
# presenter directly rather than rely on a previous security context.
'force_authn': False,
# Enable AllowCreate in NameIDPolicy.
'name_id_format_allow_create': False,
# attributes that this project need to identify a user
'required_attributes': ['username'],
# attributes that may be useful to have but not required
'optional_attributes': ['eduPersonAffiliation'],
# in this section the list of IdPs we talk to are defined
# This is not mandatory! All the IdP available in the metadata will be considered.
'idp': {
# we do not need a WAYF service since there is
# only an IdP defined here. This IdP should be
# present in our metadata
# the keys of this dictionary are entity ids
'https://localhost/simplesaml/saml2/idp/metadata.php': {
'single_sign_on_service': {
saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SSOService.php',
},
'single_logout_service': {
saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SingleLogoutService.php',
},
},
},
},
},
LOGIN_URL = '/tacdb/saml2/login/'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
MIDDLEWARE.append('djangosaml2.middleware.SamlSessionMiddleware')
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'djangosaml2.backends.Saml2Backend',
)
views.py
#csrf_exempt
#login_required
def item_list(request):
....
return render(
request, "items/item_list.html", {"filter": user_filter, "form": form}
)
urls.py
urlpatterns = [
url(
r"^tacdb/",
include(
[
path('', include('tacdashboard.urls')),
path('saml2/', include('djangosaml2.urls')),
]
)
)
]
The problem here was that I did not use the correct 'assertion_consumer_service':
'assertion_consumer_service': [
('http://localhost:8000/tacdb/saml2/acs',
saml2.BINDING_HTTP_POST),
],

Django Logger: How to log Username

I am trying to implement logging in my Django project (django 1.11, Python 3.6). I'm using default django logger.
To get the username in log, I have used django-requestlogging 1.0.1.
As of now, I don't have any user other than admin superuser.
When I'm trying a GET request on front-end side, an error occurs that says 'LogSetupMiddleware' is not callable.
What is the reason for this error? How do I make the logging work?
How do I get AnonymousUser in the logs?
File settings.py snippet:
INSTALLED_APPS= [
...
'django_requestlogging',
]
MIDDLEWARE = [
...
'django_requestlogging.middleware.LogSetupMiddleware',
...
]
LOGGING = {
'version': 1,
'formatters': {
'standard' : {
'format': '%(asctime)s %(username)s %(request_method)s',
},
},
}
As I can see here middleware class is really not callable. To fix it you have to override that class like this:
some/middlware/path.py
from django.utils.deprecation import MiddlewareMixin
from django_requestlogging.middleware import LogSetupMiddleware as Original
class LogSetupMiddleware(MiddlewareMixin, Original):
pass
And replace middleware path in settings.py by new one:
some.middleware.path.LogSetupMiddleware

how to add token auth to swagger + django rest framework?

I am using both great tools DRF and Django-REST-Swagger, however a few of my API views are under token authentication.
So now I'd like to add to my swagger doc page of my API the possibility to test those token auth api urls, including the Token header. How could I do this?.
A snapshot of my class API view is like this:
class BookList(APIView):
"""
List all books, or create a new book.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
...
Since Swagger auto detects a lot of stuff, I was expecting to notice about token auth, and ask me about token or user id in its web interface, but it doesn't. Hence I am testing it manually through CURL commands...
If you're using token authentication, you might want to look at this question
Basically, you just need to add this to your settings.py:
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'api_key': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
},
}
In your Swagger UI page you should see an Authorize button. Click that and enter your Authorization value in the input text field.
I answer myself since I made it work.
Actually Swagger settings has an option for this, api_key ->
SWAGGER_SETTINGS = {
"exclude_namespaces": [], # List URL namespaces to ignore
"api_version": '0.1', # Specify your API's version
"api_path": "/", # Specify the path to your API not a root level
"enabled_methods": [ # Specify which methods to enable in Swagger UI
'get',
'post',
'put',
'patch',
'delete'
],
"api_key": '', # An API key
"is_authenticated": False, # Set to True to enforce user authentication,
"is_superuser": False, # Set to True to enforce admin only access
}
To me it wasn't that clear, but I've just input a valid token for testing user and it worked for the auth needed views :-)
My Problem was that after activating TokenAuthentification my api urls were not shown any more in the swagger UI due to an AuthentificationError.
For me the solution was to activate both authentaction classes in the Django Rest Framework Settings:
SessionAuthentification -> for the Swagger UI
TokenAuthentification -> for the Rest Clients
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication'
)
}
The schema view needs to have the permission of AllowAny. This allows the plugin to see which endpoints are available before the user has authenticated. The end points should still be protected if they are setup correctly. Example:
#api_view()
#renderer_classes([SwaggerUIRenderer, OpenAPIRenderer, renderers.CoreJSONRenderer])
#authentication_classes((TokenAuthentication, SessionAuthentication))
#permission_classes((AllowAny,))
def schema_view(request):
generator = schemas.SchemaGenerator(
title='My API end points',
patterns=my_urls,
url="/api/v1/")
return response.Response(generator.get_schema(request=request))
It is best to remove the SessionAuthentication and only use the TokenAuthentication but that is a matter of choice, here I have removed it
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated'
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication'
)
Be sure it add 'rest_framework.authtoken' into your installed apps and remove the CsrfViewMiddleware from the middleware classes as it will no longer be needed. And the swagger settings
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'api_key': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
},
'USE_SESSION_AUTH': False,
'JSON_EDITOR': True,
}
This will make swagger populate the token into all of the example curl commands as well, which is really nice to have. Leaving the session auth in place seems to disable this.
The swagger authorization dialog asks for the api_key which needs to be provided. Can not seem improve this, will update this post if I do.
just add to you rest framework setting SessionAuthentication should be at the top
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'mymodule.authentication.CustomeAuthentication',
)
Note: SessionAuthentication will use your Django login session
CustomeAuthentication will be used for rest api for real use case.
if you are implementing the answer of #Melvic Ybanez with django-rest-swagger==2.2.0 and still doesn't work. Downgrade to django-rest-swagger==2.1.2.
Button authorize should work now.
I manage to change Swagger's default basic authentication to token authentication with this configuration but when try me button is pressed rest swagger accepts any authentication regardless of valid token.
Also note, when I added SessionAuthentication to my REST_FRAMEWORK in my settings.py, my api failed to be displayed on swagger docs.
django-rest-swagger==2.2.0
djangorestframework==3.7.7
settings.py
INSTALLED_APPS = [
'rest_framework',
'rest_framework_swagger',
'rest_framework.authtoken',
]
REST_FRAMEWORK = {
# Parser classes priority-wise for Swagger
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
'rest_framework.parsers.JSONParser',
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
# SWAGGER SETTINGS
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'api_Key': {
'type': 'apiKey',
'in': 'header',
'name': 'Token Authorization'
}
},
}
some helpful documentation https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-definitions-object
Please use rest_framework_jwt.authentication.JSONWebTokenAuthentication instead of rest_framework.authentication.TokenAuthentication.
My code is
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
'rest_framework.parsers.JSONParser',
),
}
AUTH_USER_MODEL = 'auth.User'
JWT_AUTH = {
'JWT_ENCODE_HANDLER':
'rest_framework_jwt.utils.jwt_encode_handler',
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
'JWT_PAYLOAD_GET_USER_ID_HANDLER':
'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_ALGORITHM': 'HS256',
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': True,
'JWT_LEEWAY': 0,
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=86400),
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
'JWT_ALLOW_REFRESH': True,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}
SWAGGER_SETTINGS = {
'SHOW_REQUEST_HEADERS': True,
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header'
}
},
'USE_SESSION_AUTH': False,
'JSON_EDITOR': True,
'SUPPORTED_SUBMIT_METHODS': [
'get',
'post',
'put',
'delete',
'patch'
],
}
I solved the problem.