I have written a REST API for updating a user's password. Since it is impossible to unhash the password stored by django, how am I suppose to test my API besides asserting the response status_code?
You can check a user's password with User.check_password(password_to_check). This will return True if the password is correct. (see documentation here)
Note that if you have created a user in your unit test and then change a password for the user, you need to update the user reference before you can see the new password, like this:
// create self.user
// change the password to "newpassword"
self.user = User.objects.get(username="username") # get user again so that you can see updated password
self.assertEquals(self.user.check_password("newpassword"), True)
What I would do is create the following assertions in the test_password_update test:
user login
password update
user login
If the user is able to login after the password update, then you are safe and the API works correctly.
Related
I have an extended django user model, and I want the user to first enter their username then be redirected to another page that asks for their password. Somewhat similar to how amazon handles user login.
Do I just use 2 separate views and pass the username as an input to the password view?
Second question, can this cause a security problem?
def usernameView(request):
# get username and redirect to password
def passwordView(request, username):
# get password if correct login
I am using DRF with auth toolkit and it is working fine. However, I want to have a second login api so a user can log in using username and pin number. It is cos we have a USSD application and it is easier to give them a pin based login system.
Currently, I have the following URL that, when called, generates token:
url(r'^signin/', include('oauth2_provider.urls', namespace='oauth2_provider')),
For the ussd app, I want something like that but the auth2 should check pin field, defined in a separate model defined as follows:
class Members(models.Model):
pin=models.IntegerField()
user=models.ForeignKey(User)
I am a little lost where to start.
Using this answer as a base to answer this question, and Django's documentation.
I would say you'd want to create a custom authentication backend, and you'd want a custom user model with two passwords, or using a one-to-one relationship to add the additional password field, something like so:
from django.contrib.auth.models import AbstractBaseUser
class UserExtension(AbstractBaseUser):
user = models.OneToOneField(User)
...
Inheriting from the AbstractBaseUser should add a password field like the user model, (although I haven't tried this). If you prefer the custom user approach, I actually have a github repo that has a custom user app, so if you'd like to get any ideas of how to achieve this check it out.
Or have a look through the documentation.
Either way, once you've got your two passwords, you need to decide which one to use as the pin. If you're using oauth for the pin field and the web applicaiton with the password, I would probably use the standard user password for the pin login, as that way you don't need to change the oauth package to work with your new password. Then for your web application build a custom login. To do this create a custom authentication backend along the lines of:
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
class AuthBackend(object):
supports_object_permissions = True
supports_anonymous_user = False
supports_inactive_user = False
def get_user(self, user_id):
return User.objects.filter(pk=user_id).first()
def authenticate(self, username, password):
user = User.objects.filter(username=username).first()
if not user:
return None
# this is checking the password provided against the secondary password field
return user if check_password(password, user.userextension.password) else None
Then you need to add this authentication backend to your settings:
AUTHENTICATION_BACKENDS = ('myapp.backends.AuthBackend',)
Then create the web application login (as per the stackoverflow answer above):
from django.contrib.auth import authenticate, login
def my_login_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
else:
# Return a 'disabled account' error message
...
else:
# Return an 'invalid login' error message.
...
You should now have a custom authentication login for the web application using your password2 field, and you can use the oauth authentication to work with the standard Django password in which you're going to store the pin. Which I think is what you're trying to do?
NOTE: All of the above I haven't tested, so this may not work perfectly, but it should hopefully be able to at least point you in the right direction and give you a few ideas. If I'm understanding your problem correctly, this is the sort of approach that I would take to tackle the problem.
Given an username and password, I want to authenticate against a LDAP directoy with these credentials in one of my Django apps. I've been taking a look Django-auth-ldap but I still don't know how to use it properly once I've configured it.
On the one hand, I receive a JSON with username and password and I need to add an user to LDAP with these credentials. And, on the other hand, in subsequent request, I need to check that the user who makes the request belongs to LDAP (and only LDAP not the own Django backend, so if there's an user with the same credentials in the Django users database should't be allow to continue).
I've tried something like this to add an user:
authbackends.py
class CustomLDAPBackend(LDAPBackend):
def authenticate(self, username, password, **kwargs):
# Add user to LDAP
user = LDAPBackend.authenticate(self, username, password)
return user
So I can import it in my view to add the user to LDAP.
Could anyone outline a solution?
I am trying to make a website, where people only put their email addresses and they are logged in with cookies and all. At a later stage, i will ask them provide password and names, but NO username will be used. I am trying to do this with django-registraition, but i get errors and i have a few problems.
First to disable usernames as a login feature, i put str(time()) instead of username - i was looking for something that will change every time.
However, when I skip the authentication (which i currently don't need) i get error:
'RegistrationProfile' object has no attribute 'backend'
Alternatively, i can leave the authentication but then i don't know how to authenticate it only with email and no password. Also, i don't know how to make the next line work:
auth.login(request, ProfileUser)
If anyone can get me out of here, it would be awesome. Here is some code:
my form Class:
class RegistrationFormCustom(forms.Form):
email = forms.EmailField()
def do_save(self):
new_u = User(username=str(time()),email= self.cleaned_data.get('email'),)
new_u.save()
new_p = Profile.objects.create_profile(new_u)
new_p.save()
return new_p
my view:
def registerCustom(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None,
initial={}):
form = RegistrationFormCustom(initial=initial)
if request.method == 'POST':
form = RegistrationFormCustom(initial=initial, data=request.POST)
if form.is_valid():
profile = form.do_save()
profile = auth.authenticate(username = profile.user.email, password = form.cleaned_data.get('pass1'))
print(profile)
auth.login(request, profile)
return redirect('/')
else:
pass
return render_jinja(request, 'registration/registration_form.html',
type="register",
form = form
)
and i will post any other snipped required happily
You're getting the 'RegistrationProfile' object has no attribute 'backend' error because the user is not yet authenticated. To log someone in, you have to call the authenticate method first, which requires a password. So, what you can do instead, is this:
from django.contrib.auth import load_backend, login, logout
from django.conf import settings
def _login_user(request, user):
"""
Log in a user without requiring credentials (using ``login`` from
``django.contrib.auth``, first finding a matching backend).
"""
if not hasattr(user, 'backend'):
for backend in settings.AUTHENTICATION_BACKENDS:
if user == load_backend(backend).get_user(user.pk):
user.backend = backend
break
if hasattr(user, 'backend'):
return login(request, user)
Then, to log someone in, just call the _login_user function with the request and User model. (This will be profile.user in your case, probably) Do this instead of calling auth.login. I'm not sure on how you're going to determine whether this is a valid user or not, without a password or username, but I'll leave that to you. If you still have trouble, let me know.
Short Explanation:
What basically happens here is that Django requires a user to be authenticated in order to be logged in via the login function. That authentication is usually done by the authenticate function, which requires a username and password, and checks whether the supplied password matches the hashed version in the database. If it does, it adds an authentication backend to the User model.
So, since you don't have a password and username, you just have to write your own method for adding the authentication backend to the User model. And that's what my _login_user) function does - if the user is already authenticated, it just calls login, otherwise, it first adds the default backend to the User model, without checking for a correct username and password (like authenticate does).
For others reading this thread, I got a similar error message when I was using User.objects.create() instead of User.objects.create_user(). Basically, the first method was setting a clear password whereas create_user encrypts the password. Clear passwords will fail to authenticate. Check your database, if you have passwords set in the clear, then it's likely you need to use create_user() instead.
The author's request could be fixed by simply setting a default user and password using create_user() instead of just user.save().
You can create a known password (put it in settings.py ) and use that as though the user entered it. Create the user with this and authenticate the user with this.
In my custom authentication backend I extract the username, email, first and last name from an LDAP response and try to stick them into a newly generated User object if the user doesn't yet exist:
user = User(username=username, email=result[0][1].get('mail')[0], first_name=result[0][1].get('givenName')[0], last_name=result[0][1].get('sn')[0])
user.save()
And another variant I tried:
user = User.objects.create_user(username, result[0][1].get('mail')[0])
user.first_name = result[0][1].get('givenName')[0]
user.last_name = result[0][1].get('sn')[0]
user.save()
While the username and email show up in the admin after the user's initial successful authentication attempt I can't get the first and last name to display. Logging the values from the LDAP response shows that these exist.
Any idea what's going wrong here?
Ok, it was indeed my own stupidity: should not only have restarted the frontend webserver but also uWSGI! I could add to my defense that these are my baby steps with uWSGI...