I'm using django-all auth with GMail login.
There one of my view that will receive HTTP-POST from a Hotspot login page in other server (actualy it's mikrotik hotspot redirect).
I need to read their posted data AFTER social login.
I read https://stackoverflow.com/a/32250781/5901318
Looks like the safest way is to store the POST data in session, and later my view will get it from request.session
but I don't know how to 'store data safely in request.session before the authentication occurs'.
def my_login_required(function):
#https://stackoverflow.com/a/39256685/5901318
def wrapper(request, *args, **kwargs):
decorated_view_func = login_required(request)
if not decorated_view_func.user.is_authenticated:
if request.method == "POST" :
print('my_login_required POST:',request.POST.__dict__)
print('my_login_required ARGS:',args)
print('my_login_required KWARGS:',kwargs)
print('my_login_required SESSION:',request.session.__dict__)
wrapper.__doc__ = function.__doc__
wrapper.__name__ = function.__name__
return wrapper
##receiver(user_logged_in)
#csrf_exempt
#my_login_required
def hotspotlogin(request,*args,**kwargs):
print('HOTSPOTLOGIN')
I tried to access it using requests :
r = requests.post('http://mysite:8000/radius/hotspotlogin/', json={"NAMA": "BINO"}, headers = {'Content-type': 'application/json', 'Accept': 'text/plain'})
but in django shell I only got :
my_login_required POST: {'_encoding': 'utf-8', '_mutable': False}
my_login_required ARGS: ()
my_login_required KWARGS: {}
my_login_required SESSION: {'storage_path': '/opt/djangos/radius03/mysessions/', 'file_prefix': 'sessionid', '_SessionBase__session_key': None, 'accessed': True, 'modified': False, 'serializer': <class 'django.core.signing.JSONSerializer'>, '_session_cache': {}}
Kindly please give me any clue to do that.
Sincerely
-bino-
Got priceless help from a friend, and here is the solution.
def my_login_required(function):
def wrapper(request, *args, **kwargs):
old_data=dict()
try :
old_data['POST'] = dict(request.POST)
except :
old_data['POST'] = dict()
try :
old_data['GET'] = dict(request.GET)
except :
old_data['GET'] = dict()
old_data['method'] = request.method
decorated_view_func = login_required(request)
if not decorated_view_func.user.is_authenticated: #Only if user not authenticated
request.session['old'] = old_data #put old data in request.session['old']
return decorated_view_func(request) # return redirect to signin
return function(request, *args, **kwargs)
wrapper.__doc__ = function.__doc__
wrapper.__name__ = function.__name__
return wrapper
#my_login_required
def testview(request,*args,**kwargs):
print('SESSION DATA:', request.session.get('old')) #take original post/get data from request.session['old']
Related
I have the following code that sends requests to check JWT token, then authorize user and return authorized session with Access token, Refresh Token and Session ID.
#csrf_exempt
def new_login_view(request, *args, **kwargs):
def convert_data(req):
data = {
"email": req.data['username'],
"password": req.data['password'],
}
try:
data["language"] = request.LANGUAGE_CODE
except:
data["language"] = request.POST.get('language', 'en')
return data
if request.user.is_authenticated and not request.META.get('HTTP_X_AVOID_COOKIES'):
return HttpResponseRedirect(request.GET.get(KEY_NEXT, '/'))
if request.method == 'POST':
request_data = convert_data(request)
# request to Accounts API to check if user exists
response = send_service_request(EnumAuthUrls.sign_in.value,
json_data=request_data,
original_response=True)
if isinstance(response, dict):
return JsonResponse(response)
if response.status_code == 200:
tok_ac = response.headers.get(HEADER_ACCESS_KEY)
tok_ref = response.headers.get(HEADER_REFRESH_KEY)
# checking JWT token
user = ApiAuthenticationBackend().authenticate(request, tok_ac)
# creates session
data = login_session(request, response, user)
data['user_id'] = request.user.id
data['account_id'] = request.user.profile.account_id
data['balance'] = request.user.balance
if request.META.get('HTTP_X_AVOID_COOKIES'):
return JsonResponse(data)
response = AuthResponse(
data=data,
ssid=request.session.session_key,
access_token=tok_ac,
refresh_token=tok_ref,
)
return response
else:
return ErrorApiResponse(response.json())
service = urllib.parse.quote_plus(request.build_absolute_uri())
return HttpResponseRedirect(settings.ACCOUNTS_URL + f'login/?service={service}')
Here's the code of login_session fucntion:
def login_session(request: HttpRequest, response: HttpResponse, user):
request.user = user
request.session.create()
base_data = response.json().get(KEY_DATA)
return request.user.serialize(request, base_data, token=True)
And here's the class AuthResponse that is eventually based on HttpResponse:
class AuthResponse(SuccessResponse):
def __init__(self, data={}, ssid='', access_token: str = '', refresh_token: str = '', **kwargs):
super().__init__(data, **kwargs)
if ssid:
logger.debug(f'Setting {settings.SESSION_COOKIE_NAME}: {ssid}')
self.set_cookie(key=settings.SESSION_COOKIE_NAME,
value=ssid)
if access_token:
self.set_cookie(key=settings.ACCESS_KEY_COOKIE_NAME,
value=access_token)
if refresh_token:
self.set_cookie(key=settings.REFRESH_KEY_COOKIE_NAME,
value=refresh_token)
The problem is that looks everything good on the browser side, I get all needed cookies (access token, refresh token and session id) however after trying logging in I get redirected to the main page.
There was problem in the beginning with setting cookies, but then I found out that I should not use SESSION_COOKIE_DOMAIN if it's local. Thus all cookies came up without problem but it didn't resolve situation with authorization.
While setting cookies with self.set_cookie() I tried to use secure=True, samesite='Lax', httponly=True, and all other parameters but it didn't help.
Does anyone knows what else I can try in order to fix it?
Well, I found what was wrong!
There was middleware that supposed to check token from another service. However it was checking old token, instead of new one.
So once I changed it and started to check new token - it was working just fine.
So if there's no other solutions, make sure you have checked middleware or other code where it could affect on whole system.
I have a post method under View Set. I need to write a unit test case for the method. when I pass param its give None. How should I pass both param and data(payload).
views.py
#action(detail=True, methods=['post'])
def complete_task(self, request, *args, **kwargs):
"""
Method for complete the task
input post request : task_id : str, variable_return:boolean, request data: dict
output Response : gives whether task is completed or not
"""
try:
get_task_id = self.request.query_params.get("task_id")
get_process_variables = request.data
print(get_task_id)
print(get_process_variables)
complete_task = CamundaWriteMixins.complete_task(url=CAMUNDA_URL, task_id=get_task_id,
process_variable_data=get_process_variables)
print("compl", complete_task)
return Response({"task_status": str(complete_task)})
except Exception as error:
return Response(error)
test.py
def test_completed_task(self):
self.client = Client()
url = reverse('complete-task')
data = {"variables": {
"dept_status": {"value": "approved", "type": "String"}}
}
response = self.client.post(url, data=data, params={"task_id": "000c29840512"},
headers={'Content-Type': 'application/json'})
print(response.data)
self.assertTrue(response.data)
I have tried above test case method which is getting request data but I got param None.
Thanks in Advance,.
if you just modify your request a bit and add query param as part of your url then i guess you are good to go.
Example:
response = self.client.post(f'{url}?task_id=000c29840512', data=data,
headers={'Content-Type': 'application/json'})
you can refer the official documentation for the example: https://docs.djangoproject.com/en/4.0/topics/testing/tools/
I'm currently struggling to make this current unit-test pass:
def test_markNotifications(self):
request_url = f'Notifications/mark_notifications/'
view = NotificationsViewSet.as_view(actions={'post': 'mark_notifications'})
request = self.factory.post(request_url)
request.POST = {'id_notifs': "1"}
force_authenticate(request, user=self.user)
response = view(request)
self.assertEqual(response.status_code, 200)
Here's the associated view:
#action(detail=False, methods=['POST'])
def mark_notifications(self, request, pk=None):
"""
Put Notifications as already read.
"""
id_notifs = request.POST.get("id_notifs")
if not id_notifs:
return Response("Missing parameters.", status=400)
id_notifs = str(id_notifs).split(",")
print(id_notifs)
for id in id_notifs:
notif = Notification.objects.filter(pk=id).first()
if not notif:
return Response("No existant notification with the given id.", status=400)
notif.isRead = True
notif.save()
return Response("Notifications have been marked as read.", status=200)
The problem is that even though I'm passing "id_notifs" through the request in test, I'm getting None when I do id_notifs = request.POST.get("id_notifs").
It seems that the id_notifs I'm passing in the POST request are neither in the body and the form-data. In this context, I have no idea on how to access them.
Looking forward some help, thanks.
I created the userdefined decorator to check the session is active or not. Following is the function defination
def session_required(func):
"""
Decorator to check the session is active or not for logged in user
:param func: Name of function for which you have to check the session is active or not
:return:
"""
def wrap(request, *args, **kwargs):
"""
Wrapper function for the decorator
:param request: request parameter for called URL
:return:
"""
if not request.session.get("admin_id"):
return redirect("/")
func_return = func(request, *args, **kwargs)
return func_return
return wrap
I am using this decorator on the respective function based view. At some places it works absolutely fine but when I do some POST or PUT operation then it gives me error
Forbidden (CSRF token missing or incorrect.):
My function based view is like
#csrf_exempt
#session_required
def mover_profile_handler(request):
"""
mover profile handler function for viewing and editing the details
:param request:
:return:
"""
try:
if request.method == "GET":
login_id = request.session.get("admin_id")
login_info_obj = Login.objects.get(id=login_id)
mover_info_obj = Mover.objects.get(fk_login=login_info_obj)
country_obj = Country.objects.all()
currency_obj = CurrencyType.objects.all()
subscription_detail = SubscriptionMoverDetail.objects.filter(fk_mover=mover_info_obj).order_by("-id")
# Extracting data for showing the subscription package details
current_subscription_detail = {}
subscription_detail_history = []
for index, item in enumerate(subscription_detail):
subscription_master_detail = SubscriptionMaster.objects.get(id=item.fk_subscription_master_id)
subscription_detail_json = {
"plan_name": subscription_master_detail.subscription_plan_name,
"subscription_start_date": item.subscription_date,
"subscription_end_date": item.subscription_end_date,
"amount_paid": item.amount_paid,
"users": subscription_master_detail.customer_permitted_count
}
if index == 0:
current_subscription_detail = subscription_detail_json
else:
subscription_detail_history.append(subscription_detail_json)
return render(request, "mover_profile.html", {
"mover_info_obj": mover_info_obj,
"country_obj": country_obj,
"currency_obj": currency_obj,
"login_info_obj": login_info_obj,
"current_subscription_detail": current_subscription_detail,
"subscription_detail_history": subscription_detail_history
})
elif request.method == "PUT":
request = convert_method_put_to_post(request)
mover_id = request.POST.get("id")
if Mover.objects.filter(id=mover_id).exists():
mover_info_obj = Mover.objects.get(id=mover_id)
mover_info_obj.mover_name = request.POST.get("name")
mover_info_obj.address = request.POST.get("address")
mover_info_obj.phone_no = request.POST.get("phone")
mover_info_obj.mover_size = request.POST.get("size")
mover_info_obj.reg_no = request.POST.get("reg_no")
mover_info_obj.website = request.POST.get("website")
mover_info_obj.fk_country_id = request.POST.get("country")
mover_info_obj.fk_currency_id = request.POST.get("currency")
operational_countries = request.POST.getlist("operational_countries[]")
mover_info_obj.countries_in_operation.set(operational_countries)
mover_info_obj.save()
return HttpResponse("success")
except Exception as e:
error_save(str(traceback.format_exc()))
return redirect('error_handler_500')
I tried with
#csrf_protect #csrf_exempt
in view and also tried {% csrf_token %} in html file
without using #session_required code is working absolutely fine.
So please tell me what is wrong with this stuff!!
Besides the API I created by using the django rest framework, now I want to add the existing commerial API into the backend, how to do this?
The commerial API include several part(API), for example, /prepare, /upload, /merge,/get_result, etc. They all use the "POST" indeed.
Do I need to call the commerial API directly on the frontend? Or I need to integrate the commerial API in to one backend API? Any suggestions are appreciated.Thanks.
For example:
```
class TestView(APIView):
"""
Add all the commerial API here in order
/prepare(POST)
/upload(POST)
/merge(POST)
/get_result(POST)
return Result
"""
```
Depending on your needs, I suggest making the external API calls on backend.
As a good practice, you should seperate your external API calls from your views. As it can be messy as the project gets bigger.
Check the sample code of how I manage external API calls, by seperating them to a different file, for example api_client.py
My default api_client.py looks something like this.
(You need to install "requests" pip package by pip install requests)
import requests
from django.conf import settings
class MyApiClient(object):
def __init__(self):
self.base_url = settings.EXTERNAL_API_1.get('url')
self.auth_url = "{0}login/".format(self.base_url)
self.username = settings.EXTERNAL_API_1.get('username')
self.password = settings.EXTERNAL_API_1.get('password')
self.session = None
self.access_token = None
self.token_type = None
self.token_expires_in = None
def _request(self, url, data=None, method="POST", as_json=True):
if self.session is None:
self.session = requests.Session()
if not self.access_token:
self.authenticate()
r = requests.Request(method, url=url, data=data, headers={
"Accept": "application/json",
"Content-Type": "application/json",
'Authorization': 'Token {0}'.format(
self.access_token)
})
prepared_req = r.prepare()
res = self.session.send(prepared_req, timeout=60)
if as_json:
json_result = res.json()
return json_result
return res
def _get(self, url):
return self._request(url=url, method="GET")
def _post(self, url, data):
return self._request(url=url, data=data, method="POST")
def authenticate(self):
res = requests.post(self.auth_url, json={'username': self.username,
'password': self.password},
headers={"Content-Type": "application/json"})
if res.status_code != 200:
res.raise_for_status()
json_result = res.json()
self.access_token = json_result.get('response', None).get('token',None)
def prepare(self):
something = 'bla bla'
request_url = "{0}prepare".format(self.base_url)
result = self._post(url=request_url, data=something)
return result
And in your views.py
from api_client import MyApiClient
class TestView(APIView):
api_client = MyApiClient()
def post(request, *args, **kwargs):
res1 = api_client.prepare()
res2 = api_client.your_other_method()
res3 = api_client.your_last_method()
return Response(res3)
Edited! Hope this helps now :)