Django using one time call function output as global value - python

While initializing the project, i want to call login function from projects settings for once and use the output token anywhere in project. I want to use same token whether I call again to check if token changed.
def login(creds):
r = requests.post(base_url + '/api/admin/login', json=creds, headers=headers, verify=False)
if r.status_code == 200:
token = r.json()['token']
return token
If i call function in project settings, function starts each time. I don't want to dump token to a file and read each time. Any other way? Thanks.

The best way would be to separate responsibilities for login, authentication and use of token. In practice you'd store token in session. Django provides full support for anonymous sessions, but this could limit your application in cases, when you'd like to store some data associated to that user in the local database.
What I recommend is to setup User model in the local database where you save usernames. Next you create a function for authentication:
import requests
def authentication(username, password):
creds = {'username': username, 'password': password}
headers = 'whatever'
r = requests.post(base_url + '/api/admin/login', json=creds, headers=headers, verify=False)
if r.status_code == 200:
token = r.json()['token']
# Create user in the local database if it does not exist yet
user, created = User.objects.get_or_create(username=username)
return token
else:
error = 'Something went wrong'
return error
Next you create login functionality.
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.contrib.auth import login
from django.shortcuts import render
def login(request):
# Check if user is authenticated by call is_authenticated() and
# additionally check if token is stored in session
if request.user.is_authenticated and request.session.get('token', False):
return HttpResponseRedirect(reverse('user_page'))
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
auth_data = authenticate(username=username, password=password)
if auth_data is not None:
# Save token in session
request.session['user_token'] = auth_data
# Get user and login
# Because you created a user in local database,
# you can login that user in this step
user = User.objects.get(username=username)
login(request, user)
next = request.POST.get('next', '/') if request.POST.get('next') else '/'
return HttpResponseRedirect(next)
else:
# Implement some error handling here
return HttpResponseRedirect(reverse('login'))
else:
return render(request, 'login.html', {})
The next logical step would be to control this users tokens somehow. Decorators come to rescue. You create a decorator that checks token. For example something in this sense:
import functools
from django.contrib.auth import REDIRECT_FIELD_NAME
from urllib.parse import urlparse
from django.shortcuts import resolve_url
from django.contrib.auth.views import redirect_to_login
def token_required(func, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
#functools.wraps(func)
def decorated_function(request, *args, **kwargs):
# Check if token is saved in session
# Redirect to login page if not
if not request.session.get('token', False):
path = request.build_absolute_uri()
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
return func(request, *args, **kwargs)
return decorated_function
You'd need to implement your logic here. This decorator only check if token is stored in session, nothing else. You'd then use this decorator in views as in
from django.contrib.auth import logout
#token_required
#login_required
def user_logout(request):
logout(request)
return HttpResponseRedirect('/')

Related

Django passthrough API request to hide credentials

I need to make a request to b.com/rest/foo to get json data for my application. I want to do it this way to protect the credentials rather than expose them on every page.
I found Consume an API in Django REST, server side, and serve it ,client side, in Angular and the corresponding answer at https://stackoverflow.com/a/65672890/2193381 to be a great starting point.
I created a local url to replicate the data that the external server will return and then tried the following
import requests
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from django.views.decorators.cache import never_cache
#never_cache
#login_required
def fakedata(request, item):
return JsonResponse({'item': item})
def getdata(request, item):
url = f"http://localhost:8080/rest/{item}"
username = os.getenv('SITE_USERNAME', None)
password = os.getenv('SITE_PASSWORD', None)
userpass = dict(username=username, password=password)
data = requests.get(
url,
auth=requests.auth.HTTPBasicAuth(**userpass),
)
if data is not None and data.status_code == 200:
try:
return JsonResponse(data.json(), safe=False)
except ValueError:
print("!JSON")
return JsonResponse({})
print("!data")
return JsonResponse({})
urlpatterns = [
path('rest/<str:item>', fakedata),
path('foo/<str:item>', getdata),
]
When I test it with
python manage.py runserver 8080
and call http://localhost:8080/foo/a, I am getting back the html of the login page and not the data from http://localhost:8080/rest/a that I am expecting.
What changes do I need to make to get it to work?
I just went through Django documentation and found this useful and working.
you can first authenticate the user by using the authenticate() method then login with request and call your fakedata() function with passing the request:
from django.contrib.auth import authenticate, login
user = authenticate(request, **userpass)
if user is not None:
login(request, user)
data = fakedata(request, item)
else:
pass

Login/Logout a user using Django

I am currently working on a project with Django. I am trying to implement the ability of login and logout a user from the application using only python scripts in order to send post request from the client to the server.
I am trying to logout a user from my application but it does not seems to work. In my login function this is the method used to login a user:
# Function for user's login
#csrf_exempt
def loginUser(request):
login_user = authenticate(username=user.customer_username, password=user.customer_password)
if login_user is not None:
if login_user.is_active:
request.session.set_expiry(86400)
login(request, login_user)
print(request.user.is_active)
http_response = HttpResponse()
return http_response
The result of the print is True here which means that the login method is correct if I am not wrong. When I try to logout the user, using this method:
# Function for user's logout
#csrf_exempt
def logoutUser(request):
# Loging out the user
print(request.user.is_active)
logout(request)
http_response = HttpResponse()
return http_response
It does not logout the user and the result of the print is False here, which means that the user is not logged in. If anyone have any idea, how to solve this, it would be very helpful. Thanks.
I managed to find the answer for this problem. Because I was using a python script to send and receive post requests between the server and the client, the current user which was logged in was not saved due to after the request from the client the connection between client and server stoped. So by initialising a global Session object and later use that object to make request it allowed me to persist certain parameters across requests.
This is the code I used in the client side in order to make requests:
import requests
from requests import Session
# Create the Json object for the login
data = {'username': username,
'password': password}
r = requests.Session()
login_response = r.post("http://127.0.0.1:8000/api/login", data=data)
logout_response = r.post("http://127.0.0.1:8000/api/logout")
And this is the code I used in the server side:
# Function for user's login
#csrf_exempt
def loginUser(request):
# Get the username and password from the post request
username = request.POST['username']
password = request.POST['password']
login_user = authenticate(username=username,
password=password)
if login_user is not None:
if login_user.is_active:
login(request, login_user)
http_response = HttpResponse()
return http_response
# Function for user's logout
#csrf_exempt
def logoutUser(request):
# Loging out the user
logout(request)
http_response = HttpResponse()
return http_response
Thanks for the help!!
Commenting for visibility, Kyriakos, this is completely correct.
I also am trying to query Django by logging in and out using Django Auth (django.contrib.auth) but using a custom python script to test the posts of API's when logging in. Without the global request sessions the django.contrib.auth login will always show Anonymous User when attempting to use request.user, and this is the culprit.
import requests
from requests import Session
r = requests.Session() #SETTING GLOBAL SESSION FOR DJANGO TO READ MY SESSION
x = r.post('http://127.0.0.1:8000/newsagency/api/login/', {'username':'jake', 'password':'UOL'})
print("login response: ", x.text)
x = r.post('http://127.0.0.1:8000/newsagency/api/poststory/', {'headline' :'Story 1',
'category' : 'pol',
'region' : 'w',
'details' : 'some details'})
print("post story response: ", x.text)
def LogIn(request):
if request.method=="POST":
loginname = request.POST["name"]
loginemail = request.POST['Email']
loginpassword = request.POST["Password"]
user = authenticate(username=loginname, email=loginemail, password=loginpassword)
#login_required()
def signout(request):
logout(request)
return redirect(index)

Django: Basic Auth for one view (avoid middleware)

I need to provide http-basic-auth to one view.
I want to avoid modifying the middleware settings.
Background: This is a view which gets filled in by a remote application.
When you do a basic auth request, you're really adding credentials into the Authorization header. Before transit, these credentials are base64-encoded, so you need to decode them on receipt.
The following code snippet presumes that there's only one valid username and password:
import base64
def my_view(request):
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
token_type, _, credentials = auth_header.partition(' ')
expected = base64.b64encode(b'username:password').decode()
if token_type != 'Basic' or credentials != expected:
return HttpResponse(status=401)
# Your authenticated code here:
...
If you wish to compare to the username and password of a User model, try the following instead:
def my_view(request):
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
token_type, _, credentials = auth_header.partition(' ')
username, password = base64.b64decode(credentials).split(':')
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return HttpResponse(status=401)
password_valid = user.check_password(password)
if token_type != 'Basic' or not password_valid:
return HttpResponse(status=401)
# Your authenticated code here:
...
Please note that this latter version is not extremely secure. At first glance, I can see that it is vulnerable to timing attacks, for example.
You can try a custom decorator (as seems to be the recommended way here and here) instead of adding new middleware:
my_app/decorators.py:
import base64
from django.http import HttpResponse
from django.contrib.auth import authenticate
from django.conf import settings
def basicauth(view):
def wrap(request, *args, **kwargs):
if 'HTTP_AUTHORIZATION' in request.META:
auth = request.META['HTTP_AUTHORIZATION'].split()
if len(auth) == 2:
if auth[0].lower() == "basic":
uname, passwd = base64.b64decode(auth[1]).decode(
"utf8"
).split(':', 1)
user = authenticate(username=uname, password=passwd)
if user is not None and user.is_active:
request.user = user
return view(request, *args, **kwargs)
response = HttpResponse()
response.status_code = 401
response['WWW-Authenticate'] = 'Basic realm="{}"'.format(
settings.BASIC_AUTH_REALM
)
return response
return wrap
Then use this to decorate your view:
from my_app.decorators import basicauth
#basicauth
def my_view(request):
...
This library could be used: https://github.com/hirokiky/django-basicauth
Basic auth utilities for Django.
The docs show how to use it:
Applying decorator to CBVs
To apply #basic_auth_requried decorator to Class Based Views, use
django.utils.decorators.method_decorator.
Source: https://github.com/hirokiky/django-basicauth#applying-decorator-to-cbvs
For those that already use django-rest-framework (DRF):
DRF has a BasicAuthentication class which, more-or-less, does what is described in the other answers (see source).
This class can also be used in "normal" Django views.
For example:
from rest_framework.authentication import BasicAuthentication
def my_view(request):
# use django-rest-framework's basic authentication to get user
user = None
user_auth_tuple = BasicAuthentication().authenticate(request)
if user_auth_tuple is not None:
user, _ = user_auth_tuple

login_required django decorator

I wanted to use login_required with function based views.I gone through Django's official docs of django.contrib.auth.decorators.login_required. I could not grab it clearly.
Issue is , the control is returning back to login function even though user is already authenticated instead of going to home page.
What else changes are required to allow login using my code?
def login(request):
"""
"""
data_dict = {}
if request.POST:
req_dict = dict(zip(request.POST.keys(), request.POST.values()))
accmgr = AccountManager()
user = accmgr.validate_user(**req_dict)
if user:
ret = redirect('homepage')
else:
data_dict["msg"] = "Username or password is incorrect!"
ret = render(request, "login.html", data_dict)
else:
ret = render(request, "login.html", data_dict)
return ret
#login_required(login_url='/login')
def homepage(request):
'''
'''
return render(request, "adminpage.html", {"title":"Hello World"})
NOTE : accmgr.validate_user internally checks user.is_authenticated.There are some other checks I had to do to allow user so added custom function.
Also added LOGIN_URL in settings.py
LOGIN_URL ='/login/'
login_required decorator checks if request.user.is_authenticated() returns True. Probably the issue is, accmgr.validate_user method does not really authenticate the user.
You should call authenticate() instead, to actually log user in.
from django.contrib.auth import login as django_login
from django.contrib.auth import authenticate
def login(request):
...
req_dict = request.POST.copy()
user = authenticate(
username=req_dict['username'], password=req_dict['password'])
if user:
django_login(request, user)
...
Please see documentation for more information.

create custom decorator in django based on url paramenters

I have two login urls:
/profile/login/
/mob/profile/login/
And I have a view,
#login_required
def favorited_spreads(request ,page_template='spreads/favorited_spreads_ajax.html',
template='spreads/favorited_spreads.html',mode=None):
profile = request.user.profiles
spreads = profile.favorite_by.all()
context = {
'spreads': spreads,
'profile': profile,
}
if request.is_ajax():
template=page_template
return render(request, template,context)
And my two urls are:
url(r'^favorites/$',
'favorited_spreads', name='favorited_spreads'),
url(r'^mob/favorites/$',
'favorited_spreads',{
'template':'mobapps/spreads/favorited_spreads.html',"mode":"mob"}, name='favorited_spreads_mob'),
Now my question is, I want a decorator instead of #login_required say #custom_login_required, in which if the user is not authenticated and going to url /favorites/ here mode=None, it should be redirected to url /profile/login/.
And if he is going to url /mob/favorites/ (here mode='mob')with out authentication he should be redirected to login url /mob/profile/login/.
Any help is highly appreciated. If the question not clear please comment.
My self created a custom decorator for the same,
def custom_login_required(function,redirect_field_name=REDIRECT_FIELD_NAME):
def wrapped_func(request,mode=None, *args,**kwargs):
login_url=settings.MOBILE_LOGIN_URL if mode else settings.LOGIN_URL
if request.user.is_authenticated():
return function(request, mode=mode,*args, **kwargs)
path = request.build_absolute_uri()
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse.urlparse(login_url or
settings.LOGIN_URL)[:2]
current_scheme, current_netloc = urlparse.urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(path, login_url, redirect_field_name)
return wrapped_func
here settings.MOBILE_LOGIN_URL is /mob/profile/login/ and settings.LOGIN_URL is /profile/login/
Here I used the function django.contrib.auth.decorators.user_passes_test with relevant modification. But still I am unaware how can we reuse the django built-in decorator #login_required for the same.

Categories

Resources