curl request on django working without csrf token - django

I have a django project running on Heroku, using Django REST framework
I use the following middlewares:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Here is one of my class-based views :
class CommunityMemberView(APIView):
permissions_classes = [IsAuthenticated, ]
serializer_class = CommunitySerializer
def post(self, request, community_id=None):
"""
add the member identified by 'id'
to the community 'community_id', if it exists.
"""
data = request.data
id = data.get('id')
account = Account.objects.get(id=id)
community = Community.objects.get(id=community_id)
community.add_member(account)
serializer = self.serializer_class(community, context={'request': request})
return Response(serializer.data, status=status.HTTP_201_CREATED)
When I try to do a POST request using curl, and without any csrf token, it works fine, and I don't know why. I don't use any decorator, but if I understand the django doc correctly, I don't need to.
curl -v -H "content:application/json" -X POST -H "Content-Type:application/json" -d '{"id":"3"}' https://www.example.com/api/v1/community/2/member/ | python -m json.tool
I'm guessing there is an obvious reason, but I can't find it in the django doc nor on Stack Overflow, all the previous questions on a related topic were about why it's not working, not the contrary.
Thanks

Im assuming you are using django-rest-framework?
DRF views are wrapped w/ #csrf_excempt decorator unless you're using SessionAuthentication
See http://www.django-rest-framework.org/topics/ajax-csrf-cors/

Related

Django csrf token not refreshed after first login

I have hosted a website at http://cognezic-dev.ap-south-1.elasticbeanstalk.com/login/. On closer inspection, the form has the same csrf token across page refreshes and browsers, which I suspect to be the cause of the issue,this works fine on the local django server.Dont know where this is being cached. I have added CSRF Middleware in the settings.py too.
You can use the test credentials as username bismeet and password bis12345 for testing.
I have also tried creating a custom middleware as follows:
from django.middleware.csrf import rotate_token, CsrfViewMiddleware
from django.utils.deprecation import MiddlewareMixin
class CSRFRefresh(CsrfViewMiddleware,MiddlewareMixin):
def process_response(self, request, response):
print("Custom MiddleWare called")
rotate_token(request)
return response
But it still fails.
My settings.py with custom middleware contains:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'solvesto.middleware.csrf_refresh.CSRFRefresh'
]
If not using the custom middleware,I use:
'django.middleware.csrf.CsrfViewMiddleware'
instead of
'solvesto.middleware.csrf_refresh.CSRFRefresh'
The only last resort I see to make this work is to remove csrf altogether,which is of course,bad for security.
I removed the csrf security,no other solution,works now.

How to add an attribute to Django request object

I try to add the business attribute to the request object by using my own middleware, it nicely works with rest_framework.authentication.SessionAuthentication and I can use request.business in my views. But when I try to authenticate with JWT method (rest_framework_simplejwt.authentication.JWTAuthentication) when my middleware code is run request.user is set to AnonymouseUser so can't fetch business associated with user? Why did this happen?
# middleware.py
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.business = None
if request.user.is_authenticated and hasattr(request.user, 'business'):
request.business = request.user.business
response = self.get_response(request)
return response
Middlewares:
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'my_app.middleware.MyMiddleware',
]
rest_framework settings:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}
Unfortunately, DEFAULT_AUTHENTICATION_CLASSES are not processed in middleware process, but in the view itself. What is giving you the proper user when using session is the Django's AuthenticationMiddleware and REST Framework is just using this value when the session authentication is enabled.
To solve that, you can do one of the following:
Move adding request.business to the views (for example by adding some class that all your views will inherit from)
Move JWT authentication into Django middlewares (this has a side effect of DRF enforcing CSRF checks by default when user is logged in)

Django DRF Getting Missing Auth Headers with JWT, but With Permission and Auth on ReadOnly Views

I'm working with both the latest Django 3.0.2 and DRF 3.11.0 using djangorestframework-simplejwt 4.4.0. I did not have this problem before on a previous Django 2.2.0 project (and older lib versions), but I cannot figure out why I cannot access a view with readonly permissions set and default authentication looks okay in settings. For the simplejwt and firebaseauth library, I'm using the default settings (with correct credentials for firebaseauth, verified it works fine).
If I specify the authentication_classes as empty in the view, then it works fine, but this is just an example. I expected the defaults to work, from settings: unless I'm incorrect and should be explicitly set this way. I have also tried using gunicorn and waitress-serve, and it's the same result.
Here is the view:
class NoteList(generics.ListAPIView):
"""
List all Notes. Not used
"""
# authentication_classes = []
permission_classes = (permissions.AllowAny,)
serializer_class = NoteSerializer
def get_queryset(self):
notes = Note.objects.all()
return notes
Here is my settings:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
REST_FRAMEWORK = {
# When you enable API versioning, the request.version attribute will contain a string
# that corresponds to the version requested in the incoming client request.
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
# Authentication settings
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
'drf_firebase_auth.authentication.FirebaseAuthentication',
],
# Permission settings
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
'rest_framework.permissions.IsAuthenticated',
'rest_framework.permissions.IsAdminUser',
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
# For unit test cases
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
# Pagination
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 50,
}
If ordering the above matters, what is the correct order to have them in?
Urls, if it matters:
urlpatterns = [
path('anotes/', NoteList.as_view(), name="lame-notes"),
]
Now, when I visit the URL anonymously, I always get:
GET /v1/anotes/
HTTP 401 Unauthorized
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
WWW-Authenticate: Bearer realm="api"
{
"detail": "Invalid Authorization header format, expecting: JWT <token>."
I've been stuck on this issue for a while and could not find any similar problems to solve it. On my older Django project, I don't get this 401 Unauthorized and am able to have ReadOnly work correctly.
The view or endpoint only works correctly if I'm logged in.
If I'm missing any information please let me know.
Permissions in REST framework are always defined as a list of permission classes.
Before running the main body of the view each permission in the list is checked. If any permission check fails an exceptions.PermissionDenied or exceptions.NotAuthenticated exception will be raised, and the main body of the view will not run.
So an anonymous user can check the first item in your permission list, but fails at second. You should decide what will your default behavior will be and keep that in the list.

I'm not able to login after django version upgrade

I'm trying to upgrade an old code from django 1.5 to 1.8 for a client. The project use django.contrib.auth.views.login to verify the login. the urls.py looks as follow:
urlpatterns = patterns('',
url(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'accounts/login.html', 'authentication_form': LoginForm}, name="login"),
url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'template_name': 'accounts/logged_out.html'}, name="logout"),
)
Here is the LoginForm class:
class LoginForm(forms.Form):
username = forms.CharField(label=_("Username"), max_length=120)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
def __init__(self, request=None, *args, **kwargs):
self.request = request
self.user_cache = None
super(LoginForm, self).__init__(*args, **kwargs)
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in."))
return self.cleaned_data
and here is the list of the middlewares:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'apps.facebook.middleware.FacebookMiddleware',
)
Before the upgrade I was able to login, but after upgrading I'm no longer able to login. I got the following non-field error:
Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.
If I downgrade to django 1.5 I can login again. How can I solve the problem and why the login is not working in django 1.8.
Remove the cookie test from your login form, it isn't necessary. The check was removed from the Django login form in Django 1.7 (release notes).
You have CSRF protection enabled, which already ensures that cookies are enabled.
It's not clear why you are defining your own login form, instead of using the built in form. Using your own form means you hit problems like this, and miss out on new features like the confirm_login_allowed hook.

etag and last modified headers for django app not being set

I am trying to set the etag header using the condition decorator with Django 1.3.
I am using the following:
#condition(etag_func=profile_etag, last_modified_func=profile_last_modified)
#require_person_viewed_is_verified
def profile(request, id):
"""
Return profile for person id.
If profile is not verified only staff and self can view.
"""
user = request.user
...
with a middleware classes of
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'johnny.middleware.LocalStoreClearMiddleware',
'johnny.middleware.QueryCacheMiddleware',
# 'django.middleware.cache.CacheMiddleware',
'django.middleware.http.ConditionalGetMiddleware',
'pybb.middleware.PybbMiddleware',
)
USE_ETAGS = True
I am not getting the etag or last modified headers being set in the http response. Any ideas please?
I was using CharlesProxy (which is a great tool btw) and I had no caching enabled! School boy error I know!