We use custom authentication for our django webapp, where we hit the company ldap. Since we're using a custom backend, we seem to only be able to return None, or the username of a user from our db.
def authenticate(self,username,password):
"""
Authenticate the username credentials, and return the
"""
try:
self.ldap.simple_bind_s(username+"#"+settings.AUTH_LDAP_DOMAIN,password)
self.ldap.unbind_s()
except ldap.INVALID_CREDENTIALS:
logger.debug("Invalid credentials used for login.")
username = None
except ldap.SERVER_DOWN, e:
logger.debug("Ldap server is down.")
username = None
return username
Clearly there are three different use cases here - one where it works, one where it doesn't because the credentials are invalid, and one where the server is down. Django's custom backend seems to only really handle two of these though - invalid credentials, or working ones. How would I redirect to an error page, or inform the user that ldap is down?
I'd look at raising a custom exception in your auth backend, and catch it in your login view. Returning None from your auth backend simply means "I couldn't authenticate these credentials - try the next backend".
So, in pseudo-code,
class LoginView(TemplateView):
def post(self, request):
try:
user = authenticate(request.POST['username'], request.POST['password'])
except MyCustomLdapError:
return HttpResponseRedirect('ldap_server_unavailable.html')
else:
if user:
return HttpResponseRedirect('logged_in.html')
else:
return HttpResponseRedirect('login_failed.html')
In real life, you'd of course use a form to validate some of this - but you get the idea.
Related
Because I'm mixing things up and just making myself more confused, maybe someone can actually guide me through it.
I need to make a Django REST API that requires login. However the User table already exists in a Postgres database. I think token-based authentication is most suitable, however, those tokens don't exist yet.
(Login once to retrieve/create a token, check the token on each request)
How can I use a POST request to submit login details purely for verifying the user?
How would I generate a token upon successful login, and should I store it in a new Token table?
After this, how can I use the token to provide authentication/authorization on API data requests?
All examples I can find use the default Django User model or don't go into enough detail, which I can't use in this case.
I've made a custom authenticator that checks the username and password, however I can't get through the next steps.
from api.models import LogonAccount
from rest_framework import authentication
from rest_framework import exceptions
import bcrypt
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
username = request.data.get('username') # get the username request header
password = request.data.get('password') # get the password request header
if not username or not password: # no username or password passed in request headers
return None # authentication did not succeed
try:
user = LogonAccount.objects.get(username=username)
if bcrypt.hashpw(password.encode(), user.password.encode()):
print("succes")
return (user, None) # authentication successful
except LogonAccount.DoesNotExist:
raise exceptions.AuthenticationFailed('No such user')
Don't get confused, you are simply trying to achieve token-based authentication with DRF. DRF already comes with this feature. This article will guide you through that https://simpleisbetterthancomplex.com/tutorial/2018/11/22/how-to-implement-token-authentication-using-django-rest-framework.html
I'm looking for a way in which I can send out a user an email with a url that will log them into their user. This doesn't even necessarily have to expire. I have found tons of material on sessions however this is not what I'm after. What I need is this:
User submits something to the website
They have an account made in the background
An email is sent out containing a link "site.com/&token=foobar23124123"
The user can log in using that link (optional: for the next week)
Is there something out there that I'm missing that would help me or would I have to implement my own solution? Could I potentially just include the token from Django REST framework's Tokens?
Thank you for taking the time to read my question.
I'm Using Django 1.9 and Python 2.7
I don't think there is something for authenticating users using url get-parameters. AFAIK Django REST framework's Tokens uses HTTP headers for tokens.
You can write your own auth backend, it's quite easy. Here is an example
myproject/setting.py
AUTHENTICATION_BACKENDS = [
'myproject.backends.UrlTokenBackend',
'django.contrib.auth.backends.ModelBackend'
]
myproject/backends.py
class UrlTokenBackend(ModelBackend):
def authenticate(self, token):
try:
user = User.objects.get(token=token)
except User.DoesNotExist:
return None
if not user.is_active:
return None
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Now when you will call authenticate and login function Django will check your user against each of your backends. You can manually login user like this (this is view function):
from django.contrib.auth import authenticate, login
def user_auth(request):
token = request.GET.get('token')
user = authenticate(token=token)
login(request, user)
return redirect('index')
Update
Or you can use this hack and do only this (without custom backend):
def user_auth(request):
token = request.GET.get('token')
user = User.objects.get(token=token)
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
return redirect('index')
I know there is documentation for this question here https://docs.djangoproject.com/en/1.9/topics/auth/customizing/, however I need some clarification on my use case.
I basically have an api that I created. What this api does is submit user form data to an external url, and once submitted, the data is saved to that external urls database.
I can then get a specific user through another api call. The user is returned as an object. Now I am trying to log that user in on my end using the django.contrib.auth through my system but what I have isn't working.
Here is my process:
A. I added a custom authentication backend to my settings.py file
AUTHENTICATION_BACKENDS =(
'new_app.backends.SettingsBackend',
'django.contrib.auth.backends.ModelBackend',
)
B. In my new_app's backend file, I have the following code:
class SettingsBackend(object):
def authenticate(self, username=None, password=None):
if username and password:
user = api.get_user()
#add pk to user object to satisfy django error warning
user.pk = user.unique_field
return user
return None
def get_user(self, user_id):
try:
if self.user.pk == user_id:
return self.user
except:
return None
In a signin view, I am trying to log the user in using this:
def login_user(request, username, password):
from django.contrib import auth
if not username and password:
raise ValueError("Invalid username and password")
auth_user = auth.authenticate(username=username, password=password)
if auth_user is None:
raise ValueError("Invalid username and password")
auth.login(request, auth_user)
return auth_user
I get the error saying:
'api.get_user()' object has no attribute 'save'
The error is occuring on the auth.login(request, auth_user) line.
I think the reason is because the django.contrib.auth thinks my user object is a django model. So, the question is how should I tackle this to make it work given my scenario? Thanks for the time.
I'm not an expert at django subtleties (as it's been a while I left django for microframeworks).
That said, if I were you, what I'd do is to make sure that whatever api.get_user() is returning, implement whatever API django is expecting from that object, which means starting with an empty save() method on it.
But I'd also do standard django stuff, like #RA123 suggests in a comment, by setting AUTH_USER_MODEL up.
this is the code :
def openid_done(request, provider=None):
"""
When the request reaches here, the user has completed the Openid
authentication flow. He has authorised us to login via Openid, so
request.openid is populated.
After coming here, we want to check if we are seeing this openid first time.
If we are, we will create a new Django user for this Openid, else login the
existing openid.
"""
if not provider:
provider = request.session.get('openid_provider', '')
if hasattr(request,'openid') and request.openid:
#check for already existing associations
openid_key = str(request.openid)
#authenticate and login
try:
user = authenticate(openid_key=openid_key, request=request, provider = provider)
except:
user = None
if user:
login(request, user)
if 'openid_next' in request.session :
openid_next = request.session.get('openid_next')
if len(openid_next.strip()) > 0 :
return HttpResponseRedirect(openid_next)
return HttpResponseRedirect(LOGIN_REDIRECT_URL)
# redirect_url = reverse('socialauth_editprofile')
# return HttpResponseRedirect(redirect_url)
else:
return HttpResponseRedirect(LOGIN_URL)
else:
return HttpResponseRedirect(LOGIN_URL)
and the code use like this :
authenticate(openid_key=openid_key, request=request, provider = provider)
Is it right ?
I think the code must be like this :
user = authenticate(username='john', password='secret')
Does authenticate have the argument openid_key,provider ?
Should i Rewrite authenticate my myself to handle it .
thanks
No, django does not have an authenticate function that expects openid_key, provider arguments.
grep -r "openid" django returns nothing for version 1.2.3
What you are looking at is a custom authentication backend, like the one found on github here: https://github.com/agiliq/Django-Socialauth/blob/master/socialauth/auth_backends.py
class OpenIdBackend:
def authenticate(self, openid_key, request, provider, user=None):
try:
assoc = UserAssociation.objects.get(openid_key=openid_key)
return assoc.user
#....
Next time you notice that a function isn't being used normally, you should start wondering if it's even the same one you are thinking about : )
You should look at where the authenticate function was imported from, and you'd see it's not django project code.
Hay I was wondering if anyone knew a better way to do this.
def login_user(request):
username = request.POST.get('username')
password = request.POST.get('password')
user = User.objects.filter(username=username)
if user:
user = user[0]
if user.password == generate_password(password):
return HttpResponse("password fine")
else:
return HttpResponse("password incorrect")
else:
return HttpResponse("no user found by that username")
and the generate_password function is just
generate_password(string):
return hashlib.sha224(str(string)).hexdigest()
Any ideas would be great.
Thanks
Why don't use Django auth default views ?
the only amelioration i see is use get instead of filter (it will save you one line)
user = User.objects.get(username=username)
Looking at the level of control you want to have, you'll want to make use of the authenticate and maybe login functions in django.contrib.auth. These are the main functions for accessing authentication. I should stress that you really, really should use these instead of finding the user and checking the password hash manually. There are a number of reasons why:
they will make use of whatever authentication backend you or someone else in the future have installed
your version will be far less tested than Django's and is more likely to open security holes
it's quicker, shorter, more flexible and more readable
Django's auth app will probably change in the near future, sticking to authenticate will help you migrate to the new auth app when it gets written/committed/released
If you do want to rewrite the way a user is found and authenticated, write your own Authenticate backend, which will be used when you call authentication (even in someone else's app, like the admin). This is the only place you should rewrite authentication in Django.
The following examples are from the Django auth docs.
1. Checking a user's password:
from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
if user.is_active:
print "You provided a correct username and password!"
else:
print "Your account has been disabled!"
else:
print "Your username and password were incorrect."
2. Custom authentication backend:
Here is a backend that uses the same authenticate method as Django's, which you can find at django.contrib.auth.backends.ModelBackend:
from django.contrib.auth.models import User
class MyBackend:
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(username=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
You should download django-registration and go through the code. It manages everything for you, including cleaning the code. Your original code will not handle empty submissions.
http://bitbucket.org/ubernostrum/django-registration/