How do you add csrf validation to pyramid? - python

I'm passing in a csrf_token for every post and xhr request and want to validate the token against the session csrf token. If they don't match, I throw a 401.
I've used the NewResponse subscriber in pyramid to inspect the request and validate the csrf token in the request params against the token in the session. The validation works but it still calls the view so it def does not work as it should.
Any suggestions on the proper way to do this?
#subscriber(NewResponse)
def new_response(event):
"""Check the csrf_token if the user is authenticated and the
request is a post or xhr req.
"""
request = event.request
response = event.response
user = getattr(request, 'user', None)
# For now all xhr request are csrf protected.
if (user and user.is_authenticated()) and \
(request.method == "POST" or request.is_xhr) and \
(not request.params.get('csrf_token') or \
request.params.get('csrf_token') != unicode(request.session.get_csrf_token())):
response.status = '401 Unauthorized'
response.app_iter = []

The NewResponse subscriber is called after your view is invoked.
You want to be using an event that is invoked earlier, for example NewRequest or ContextFound. In Pyramid 1.0, you'll need to use ContextFound to properly handle things because you cannot raise exceptions in NewRequest events (this is fixed in 1.1).
The way to do this with a ContextFound event is to register an exception view for HTTPException objects like this:
config.add_view(lambda ctx, req: ctx, 'pyramid.httpexceptions.HTTPException')
Basically this will return the exception as the response object when you raise it, which is perfectly valid for HTTPException objects which are valid Pyramid Response objects.
You can then register your event and deal with the CSRF validation:
#subscriber(ContextFound)
def csrf_validation_event(event):
request = event.request
user = getattr(request, 'user', None)
csrf = request.params.get('csrf_token')
if (request.method == 'POST' or request.is_xhr) and \
(user and user.is_authenticated()) and \
(csrf != unicode(request.session.get_csrf_token())):
raise HTTPUnauthorized

Pyramid contains its own CSRF validation, which is probably a better choice.
Given your session stored CSRF tokens, this would result in the following configuration:
from pyramid.csrf import SessionCSRFStoragePolicy
def includeme(config):
# ...
config.set_csrf_storage_policy(SessionCSRFStoragePolicy())
config.set_default_csrf_options(require_csrf=True)

Related

How to log user in FastAPI requests

I am trying to log all my FastAPI requests (who requested, what and what is the status code) by creating a custom APIRoute-class. This is my custom Route-class:
class LoggingRoute(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
response: Response = await original_route_handler(request)
log_request(
user = request.user,
url = request.url,
method = request.method,
status_code = response.status_code
)
return response
return custom_route_handler
The logging would work if we don't want to include request.user to the logs. When trying to access the user, the following error is thrown:
AssertionError: AuthenticationMiddleware must be installed to access request.user
I guess the user-attribute of the request is not yet initialized inside the route handler. request.user works well when handling the request later.
My question is: is there any way for me to get access to request.user inside the custom_route_handler-function? If not, is there another way to implement the logging functionality with ability to log the user as well?
You must install AuthenticationMiddleware.
https://www.starlette.io/authentication/#users
Once AuthenticationMiddleware is installed the request.user interface will be available to endpoints or other middleware.
app = FastAPI(middleware=[Middleware(AuthenticationMiddleware)])

Django JWT auth: Why the view returns always AnonymUser?

I am using JWT auth to login users. Username and password are sent in Body, however, in the customized response, an anonymUser is always returned. I think the problem is that in settings.py stands 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', and when I generate a token before and then send it in Headers the user is identified. Bit the thing is, that I cannot use 2 views in order to generate token and decode it, everything has to be in one view and I don't know how to login the user in the view and then get token and decode it.
#api_view(('POST',))
def check_token(request):
token_refresh = RefreshToken.for_user(request.user)
print(request.user) # AnonymUser
print(request.user.id) # None
print(str(token_refresh.access_token))
data = {'token': str(token_refresh.access_token), 'refresh_token': str(token_refresh)}
aT = str.encode(str(token_refresh.access_token))
try:
valid_data = TokenBackend(algorithm='HS256').decode(aT, verify=False)
print(valid_data)
data['uuid'] = valid_data['user_id']
data['validUntil'] = valid_data['exp']
data['clientId'] = 'default'
return JsonResponse(data)
except ValidationError as v:
print("Validation error", v)
The answer can be found here
The The request.user is set by the django.contrib.auth.middleware.AuthenticationMiddleware.
So the request.user does not know about JWT as it is using Djangos authentication system. You can read about using JWT with Django here

Check if user is authenticated with django TokenAuthentication

I'm trying to develop a REST API with DRF that uses TokenAuthentication. This will be used in an android app.
I was able to authenticate a user and retrieve it's token. The problem I'm having now is with the following view:
#csrf_exempt
def foo(request):
if request.method == 'GET':
if request.user.is_authenticated():
...
do stuff
...
return HttpResponse(data, "application/json")
else:
return HttpResponse(status=401)
Basically the user should be authenticated in order to receive the data, otherwise, he will receive a 401 response.
I'm making a GET request to the proper URL with the following parameters in the Header:
content-type : application/json
authorization : Token <user token>
Which is basically what I'm doing for other Viewsets (this is not a Viewset) I have - and it works.
In this case, it's always sending the HTTP response with 401 code (user isn't authenticated).
I can't figure out if the problem is with the Header values I'm passing or if this is not the proper way to check if the user is authenticated.
Edit: if I do: "print request.user" i get AnonymousUser
Thanks!
Solved
As suggested by "ABDUL NIYAS P M" I used the APIView
Basically, I just added the #api_view(['GET']) decorator to the View.
#csrf_exempt
#api_view(['GET'])
#permission_classes((IsAuthenticated, ))
def foo(request):
if request.method == 'GET':
...
An easier way to do this is by checking if the user session is existing or not.
When DRF creates a token, it also creates the session cookie.
return HttpResponse(json.dumps({"is_authenticated": True if request.session.get('_auth_user_id', 0) else False}),
content_type='application/json')

Accessing request headers in flask-restless preprocessor

I'm building an API with Flask-Restless that requires an API key, that will be in the Authorization HTTP header.
In the Flask-Restless example here for a preprocessor:
def check_auth(instance_id=None, **kw):
# Here, get the current user from the session.
current_user = ...
# Next, check if the user is authorized to modify the specified
# instance of the model.
if not is_authorized_to_modify(current_user, instance_id):
raise ProcessingException(message='Not Authorized',
status_code=401)
manager.create_api(Person, preprocessors=dict(GET_SINGLE=[check_auth]))
How do I retrieve the Authorization header in the check_auth function?
I have tried accessing the Flask response object, but it is None during the scope of this function. The kw parameter is also an empty dict.
In a normal Flask request-response cycle, the request context is active when the Flask-Restful preprocessors and postprocessors are being run.
As such, using:
from flask import request, abort
def check_auth(instance_id=None, **kw):
current_user = None
auth = request.headers.get('Authorization', '').lower()
try:
type_, apikey = auth.split(None, 1)
if type_ != 'your_api_scheme':
# invalid Authorization scheme
ProcessingException(message='Not Authorized',
status_code=401)
current_user = user_for_apikey[apikey]
except (ValueError, KeyError):
# split failures or API key not valid
ProcessingException(message='Not Authorized',
status_code=401)
should Just Work.

Django POST request to my view from Pyres worker - CSRF token

I'm using Pyres workers to do some processing of data users enter in a form. Their processing is done by a view on my form, which I make a POST request to, with data including the data to process and a CSRF middleware token for the user. My issue is that this is apparently not enough, as Django still rejects my request with a 403 forbidden.
Relevant code:
Form handler:
def handler(request):
if(request.method == "POST"):
if(request.POST.__contains__("taskdata")):
#valid post of the form
taskdata = escape(request.POST.get("taskdata",""))
t = TaskData(data=taskdata, time_added=timezone.now(), token=request.POST.get("csrfmiddlewaretoken",""))
t.save()
r = ResQ(server="127.0.0.1:6379")
r.enqueue(TaskData, t.id)
return HttpResponse(t.id)
else:
#invalid post of the form
raise Http404
else:
raise Http404
Pyres worker job:
#staticmethod
def perform(taskData_id):
#Get the taskData from this id, test it for tasky stuff
task_data = TaskData.objects.get(pk=taskData_id)
post_data = [('id',task_data.id),('data',task_data.data), ('csrfmiddlewaretoken',task_data.token)] # a sequence of two element tuples
result = urllib2.urlopen('http://127.0.0.1:8000/tasks/nlp/process/', urllib.urlencode(post_data))
content = result.read()
return
View being posted to by that job:
def process(request):
if(request.method == "POST"):
return HttpResponse("HEY, it works!")
if(request.POST.__contains__("data") and request.POST.__contains__("id")):
#valid post to the form by the model
#taskdata = escape(request.POST.get("taskdata",""))
#data = get_times(taskdata)
return HttpResponse("Hey from process!")
#return HttpResponse(json.dumps(data))
else:
#invalid post of the form
raise Http404
else:
raise Http404
What I'm basically trying to do is save some raw data at form submission, along with the CSRF token for it. The workers then send that data + token to a processing view.
Unfortunately, posting the token doesn't seem to be enough.
Does anybody know what the csrf protection actually looks for, and how I can make my Pyres workers compliant?
(Suggested tag: pyres)
I think I see the problem.
The way Django's CSRF protection works is by generating a nonce, then setting a cookie to the value of the nonce, and ensuring the csrfmiddlewaretoken POST value matches the value of the cookie. The rationale is that it makes it a stateless system, which works without any persistent session data.
The problem is that the request you make in the Pyres worker job...
result = urllib2.urlopen('http://127.0.0.1:8000/tasks/nlp/process/',
urllib.urlencode(post_data))
...is coming from the server, not the client, so it won't have the cookie set.
Assuming the /tasks/nlp/process/ URL is protected such that it can only be accessed by the server, then it's probably simplest to make the process() view exempt from CSRF checking with...
#csrf_exempt
def process(request):
...
...otherwise you'll have to manually grab the cookie value in the handler() view, and pass it on to the Pyres worker job.
Update
To ensure the process() method can only be called by the server, one simple way would be to check the request object with something like...
#csrf_exempt
def process(request):
if request.META['REMOTE_ADDR'] != '127.0.0.1':
# Return some error response here.
# 403 is traditional for access denied, but I prefer sending 404
# so 'hackers' can't infer the existence of any 'hidden' URLs
# from the response code
raise Http404
# Now do the thing
....
...although there may be some built-in decorator or somesuch to do this for you.

Categories