I want to tag all requests with a UUID
(if the request doesn't have it in the first place).
I want to store the UUID in the session, so I wrote this middleware.
class MachineIDMiddleware:
"""
tags requests with machine UUIDs.
The machine-ID is set in the session.
"""
MID_KEY = "machine_id"
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print(request.session.get(self.MID_KEY))
if self.MID_KEY not in request.session:
# set the machine-ID for the request
# if it has not been set already (making
# sure that it is serializable).
next_id = str(uuid.uuid4())
request.session[self.MID_KEY] = next_id
return self.get_response(request)
However, from my client, I noticed that the UUID keeps changing for every request.
From my client, I noticed that the sessionid cookie also changed for every request made.
As a result, a new UUID was generated for every request.
This is not what I want, though. I want to maintain only one UUID per person (who might be anonymous).
How can I achieve this?
Thanks a lot!
EDIT
export const Adapter = axios.create({
baseURL: baseURL,
headers: {
"Content-Type": "application/json"
}
});
Adapter.interceptors.request.use(
(request) => {
const token = tokenSelector(store.getState());
if (token) {
request.headers.Authorization = `Token ${token}`;
}
return request;
},
(error) => {
return Promise.reject(error);
}
);
Adapter.interceptors.response.use(
(response) => {
return response;
},
(error) => {
// handle unauthorized errors.
if (error.response.status === 401) {
store.dispatch(clearToken());
history.replace(SLUGS.login);
}
// handle internal server errors.
if (error.response.status === 500) {
toast.dark("Something went wrong. Please try again later.");
}
// handle server ratelimits.
if (error.response.status === 429) {
toast.dark("You are being ratelimited.");
}
return Promise.reject(error);
}
);
This is how I send requests from the frontend.
I use axios. I checked my cookies in the developer tools panel
and couldn't see the sessionid cookie there.
EDIT 2
Chrome devtools shows me the following error and is not
setting the sessionid cookie properly. Is this the reason maybe?
** Answer (SOLVED)**
setting the following variables in my settings.py file
made sure that chrome set the cookies correctly.
# CORS configuration
ALLOWED_HOSTS = ["*"]
CORS_ALLOW_ALL_ORIGINS = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CORS_ALLOW_CREDENTIALS = True
SESSION_COOKIE_HTTPONLY = False
Here is your correct __call__ function
def __call__(self, request):
print(request.session.get("MID_KEY"))
if "MID_KEY" not in request.session:
next_id = str(uuid.uuid4())
request.session["MID_KEY"] = next_id.
return self.get_response(request)
The key shall be a string (constant) not a variable
Related
In my settings.py:
...
CSRF_COOKIE_HTTPONLY = True
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_SECURE = False
CORS_ALLOW_CREDENTIALS = True
authenticate.py:
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.conf import settings
from rest_framework import exceptions
from rest_framework.authentication import CSRFCheck
def enforce_csrf(request):
"""
Enforce CSRF validation.
"""
check = CSRFCheck()
# populates request.META['CSRF_COOKIE'], which is used in process_view()
check.process_request(request)
reason = check.process_view(request, None, (), {})
print(reason)
if reason:
# CSRF failed, bail with explicit error message
raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)
class CustomAuthentication(JWTAuthentication):
def authenticate(self, request):
.....
validated_token = self.get_validated_token(raw_token)
enforce_csrf(request)
return self.get_user(validated_token),validated_token
Error:
CSRF token missing or incorrect.
Forbidden: /photos/photo_support/
when I set CSRF_COOKIE_HTTPONLY = False then all work very well.
What's the reason when I set CSRF_COOKIE_HTTPONLY = True then they me throw 403 Forbidden error.
My Frontend is ReactJS.
TestMe.js:
Axios.defaults.withCredentials = true
Axios.defaults.xsrfCookieName = 'csrftoken';
Axios.defaults.xsrfHeaderName = 'X-CSRFToken';
const TestMe = () => {
....
const payHandle = () => {
Axios.post('http://localhost:8000/photos/photo_support/', {
data:data
})
.then(res => {
console.log(res.data)
})
.catch(error => alert(error.message))
}
...
I take it you are reading the CSRF token from the cookie at this point. As mentioned in the docs, setting CSRF_COOKIE_HTTPONLY=True doesn't provide you any practical benefit so if everything works with it being set to False, you should probably continue to do so.
However if you still need to set it to True, then you will need to read the value of the CSRF token from the hidden form field as mentioned in the docs again. These snippets are from the documentation:
Read the value like so:
{% csrf_token %}
<script>
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
</script>
Send it in your AJAX request like so:
const request = new Request(
/* URL */,
{
method: 'POST',
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin' // Do not send CSRF token to another domain.
}
);
fetch(request).then(function(response) {
// ...
});
I'm not well versed in React or Axios so you may have to adapt this concept to your needs.
I'm writing test for Django Rest Framework with PyTest. There was a problem with headers X-BULK-OPERATION because I can't add X-BULK-OPERATION with HTTP_AUTHORIZATION on header.
my code
#pytest.fixture(scope="module")
def session(django_db_setup, django_db_blocker):
with django_db_blocker.unblock():
user = User.objects.get(email=TEST_EMAIL)
user.set_password(TEST_EMAIL)
user.save(update_fields=["password"])
client = APIClient()
url = reverse("login_sys:login")
response = client.post(url, {
"email": TEST_EMAIL,
"password": TEST_EMAIL
})
client.credentials(**{"X-CSRFToken": response.cookies['csrftoken'].value})
yield client
client.logout()
#pytest.fixture(scope='module')
def choose_crm(session):
crm = Crm.objects.get()
url = reverse("crm:set-jwt", kwargs={"pk": crm.id})
response = session.get(url)
session.credentials(HTTP_AUTHORIZATION=response.data.get("token", None),
**{"X-BULK-OPERATION": True})
yield session
session.logout()
error
{'detail': "Header 'X-BULK-OPERATION' should be provided for bulk operation."}
Debug:print request, as you can see I have Bulk-Operation in header but still gives the error no BULK-OPERATION in header
'HTTP_AUTHORIZATION': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcm1fdXNlciI6MSwiY3JtX2lkIjoxLCJncm91cF9pZCI6MSwiZGVhbF9wZXJtIjp7ImNyZWF0ZSI6MSwiZGlzcGxheSI6MSwiZWRpdCI6MSwiZGVsZXRlIjoxLCJleHBvcnQiOjF9LCJjb250YWN0X3Blcm0iOnsiY3JlYXRlIjoxLCJkaXNwbGF5IjoxLCJlZGl0IjoxLCJkZWxldGUiOjEsImV4cG9ydCI6MX0sImxlYWRfcGVybSI6eyJjcmVhdGUiOjEsImRpc3BsYXkiOjEsImVkaXQiOjEsImRlbGV0ZSI6MSwiZXhwb3J0IjoxfSwidGFza19wZXJtIjp7ImNyZWF0ZSI6MSwiZGlzcGxheSI6MSwiZWRpdCI6MSwiZGVsZXRlIjoxLCJleHBvcnQiOjF9fQ.Wrd89a8jpRnNu1XhNsOKFAkNQR6v_Y_X0nKySocMtLM', 'HTTP_X-BULK-OPERATION': True}
url: https://translatorappeagle.cognitiveservices.azure.com/
I'm also using my key from my resources page. I get a an error 404: not found. I copied the code from the getting started section for the translation API.
Please let me know what to do.
import os, requests, uuid, json
key_var_name = 'TRANSLATOR_TEXT_SUBSCRIPTION_KEY'
if not key_var_name in os.environ:
raise Exception('Please set/export the environment variable: {}'.format(key_var_name))
subscription_key = os.environ[key_var_name]
endpoint_var_name = 'TRANSLATOR_TEXT_ENDPOINT'
if not endpoint_var_name in os.environ:
raise Exception('Please set/export the environment variable: {}'.format(endpoint_var_name))
endpoint = os.environ[endpoint_var_name]
# If you encounter any issues with the base_url or path, make sure
# that you are using the latest endpoint: https://learn.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate
path = '/translate?api-version=3.0'
params = '&to=de&to=it'
constructed_url = endpoint + path + params
headers = {
'Ocp-Apim-Subscription-Key': subscription_key,
'Content-type': 'application/json',
'X-ClientTraceId': str(uuid.uuid4())
}
# You can pass more than one object in body.
body = [{
'text' : 'Hello World!'
}]
request = requests.post(constructed_url, headers=headers, json=body)
response = request.json()
print(json.dumps(response, sort_keys=True, indent=4, separators=(',', ': ')))
i am using django-rest framework and i am able to get and set the custom headers using the below META information,
class log_middleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self,request):
# set thread local values
# This will execute before every request
correlation_id = request.META['HTTP_X_REQUEST_ID'] if 'HTTP_X_REQUEST_ID' in request.META else str(uuid.uuid4())
request.META['HTTP_X_REQUEST_ID'] = correlation_id
#logger.debug("Entered service")
response = self.get_response(request)
response['HTTP_X_REQUEST_ID'] = correlation_id
#logger.debug("Processed response")
return response
Now in my views.py i am able to get this header as request.META['HTTP_X_REQUEST_ID']. and it is available in the response header
But when I try to log the http header values in uwsgi using the below config,it has '-' empty value field. Because uwsgi has only actual request headers in %var.XXX variable and response headers goes to %headers and it shows only count and the actual values.
Issue: https://github.com/unbit/uwsgi/issues/1407
So Is there any way in django to append the data in actual request header instead of response header?
[uwsgi]
master = 1
memory-report = true
module = my_service.wsgi
http = 0.0.0.0:8080
max-requests = 50
processes = 16
log-format = { "ctime": "%(ctime)", "addr": "%(addr)", "method": "%(method)", "uri": "%(uri)", "correlation_id": "%(var.HTTP_X_REQUEST_ID)" }
But the same thing works if i set the header HTTP_X_REQUEST while sending the request itself from the rest client utils.
If you need middleware, you can use this:
middlewares.py:
def add_header_middleware(get_response):
def middleware(request):
request.META['hello'] = 'world'
response = get_response(request)
response['world'] = 'hello'
return response
return middleware
views.py:
#api_view(['GET'])
def sample_view(request):
return Response(request.META['hello'])
settings.py:
MIDDLEWARE = [
# ...
'your_app.middlewares.add_header_middleware'
]
My setup (local) is the following:
Vue.js running on localhost:8080 (npm run serve)
REST API built with Django running on localhost:8000 (./manage-py runserver)
In order to enable this to work I've made the following additions:
ALLOWED_HOSTS = [
...
'localhost',
'localhost:8000',
'localhost:8080',
]
INSTALLED_APPS = [
...
'rest_framework',
'corsheaders',
]
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
CORS_ORIGIN_WHITELIST = (
'localhost:8080',
'localhost:8000',
)
CORS_ALLOW_CREDENTIALS = True
from corsheaders.defaults import default_headers
CORS_ALLOW_HEADERS = default_headers + (
'credentials',
)
One of my API functions:
#ensure_csrf_cookie
def try_login(request):
# this is just to get the initial CSRF token:
if request.method == "GET" or request.method == "OPTIONS":
return JsonResponse({'status': 'ok'})
# else, an actual login request:
else:
data = JSONParser().parse(request)
user = authenticate(request, username=data['user'] , password=data['pass'])
if user is not None:
login(request, user)
return JsonResponse({'login_succ': 'ok'});
else:
return JsonResponse({'login_succ': 'fail'});
Finally, in Vue:
api: function(endpoint, method, data) {
var headers = new Headers();
headers.append('content-type', 'application/json');
if (... this is not the first request ever ...)
{
csrftoken = document.cookie.replace(/(?:(?:^|.*;\s*)csrftoken\s*\=\s*([^;]*).*$)|^.*$/, "$1");
headers.append('X-CSRFToken', csrftoken);
}
method = method || 'GET';
var config = {
method: method,
body: data !== undefined ? JSON.stringify(data) : null,
headers: headers,
};
config['credentials'] = 'include';
return fetch(endpoint, config)
.then(response => response.json())
.catch((error) => { console.log(...); });
},
trylogin: function() {
// initial request: just to get the CSRF token
this.api(".../login/", "GET").then(
response => {
this.api(".../login/", "POST", {'username': ..., 'password': ...} ).then(
response => {
if ("login_succ" in response && res["login_succ"] == "ok")
{} // user is logged in
}
);
}
);
}
What happens now, afaiu, is that my initial API request (which does not have to be pointed to the endpoint equal to the subsequent POST request, right?) gets the CSRF token as a cookie. Every subsequent request reads this cookie and sets the X-CSRFToken header. The cookie itself is also being sent in the subsequent requests. I do not understand why is the token needed in both places.
Is this approach correct? Is everything I've done necessary? (Are there redundant parts?) I'm especially interested in the way that I should acquire the token in the first place, and in general with the token's lifecycle.
Thank you.