Creating Users & Sending Password Resets in Django - python

I apologize if there has been a substantial answer on this already, I have searched for quite a while and can't find any helpful answers.
I have a django project that has varying levels of account access. I have a group of 'Managers' and I want to allow them to manage user accounts.
I want the Managers to be able to create the user account objects for the users. However, I don't want them to have to deal with creating their passwords and sending them to the users (for obvious reasons). This way, managers can impersonate users to get work done on their behalf, and users maintain password control and ownership of their data.
The idea is that managers create the account, and when the account is created Users will be sent a password reset form (same as django's out-of-box auth) which will allow them to set their passwords.
My code looks similar to below (omitted non-imperative stuff)
from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordResetForm
#manager_required
def manager_add_users(request):
add_user_form = manager_add_user_form(request.POST)
new_user_name = add_user_form.cleaned_data['user_name']
new_user_email = add_user_form.cleaned_data['user_email']
new_user = User.objects.create_user(
username = new_user_name,
email = new_user_email,
)
new_user.save()
set_password_form = PasswordResetForm({'email': new_user.email })
if set_password_form.is_valid():
print 'Reset Form Is Valid'
set_password_form.save(
request= request,
use_https=True,
from_email="support#org.com",
email_template_name='registration/password_reset_email.html')
The account creates properly and everything runs without error. The issue is that although the form is valid (reset form is valid statement prints) it is not actually sending the reset form. There are no form errors.
However, in testing when I initialize the form with an address that already exists in the system like this:
set_password_form = PasswordResetForm({'email':'existing_address#example.com'})
The password reset form sends without error. So it only works with existing user email addresses in the system, but although the user has been created and the .save() method called, it's still not catching it (The users are marked as 'Active' upon creation, so they should be able to be found)
I'm a bit at a loss. I can think of a few ways that I could get around this issue, but this seems the most direct way of doing it and I'm really not entirely sure why it doesn't work.
Yes, I am able to send messages. I am using django's backend mail for testing:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Glancing at the source code, it looks like Django is ignoring the request because the password is blank.
Try setting a temporary password (using, say, django.utils.crypto.get_random_string()) before saving the user.

Related

How to implement OTP based verification before letting the user to create a new password using pyotp?

I am very new to Django rest framework. I am building APIs for my mobile application.
In forgot password module, I have the below flow
Ask user to enter mobile
check existence
If exists - > send OTP
Verify and let user create a new password.
But in this case, I would like to know the way of handle the below situation.
When one user requests otp and waiting for it to verify, meanwhile another user requests for OTP
At this time, how to handle the both users?
I thought of
creating the dictionary and save the user id as key and otp as value in views.py to verify particular user.
store the otp temporarily until it verifies.
Which is the secured way and any alternative for this kind of scenario?
You should create a table look like this:
===== UserOTP =====
user: foreign-key to user
code: CharField, random generated code here (or token)
create_date: auto-fill created datetime
used_date: nullable datetime field
Then on each password reset request create a row on this table, send generated code to user via SMS or Email,
And then on another end-point receive the code from user and check it (for expiration and used before, belongs to this user and etc.) then continue password reset process.
There is a better way to do this, since otp are for temporary use, there is no use case for storing them in database, we can use hashlib.blake2s(b'otp', key=b'secretkey').hexdigest() to generate a hashed string and send it to user and then accept the same hashstring in the otp verification request and check for truthy of hashstring generated from user shared otp, this is oneway to handle otp verification.
The hashlibs blake2s accepts only bytestring, and you can also use any other hashing algorithm for this purpose. I am also open to ideas if there are any better ways to do this, please update in comments.
Using sessions would be better choice as it works for both django and djangorestframework.

How can I connect myself as an other user?

Hello I am a developper of a django project and I have to check that my dev is okay.
To do this, I have to connect myself as a user but I have just his email not his password. I found in the table User this the email and the password but the password begins by this : pbkdf2_sha256 ...
So I guess the password is encrypted. Is there a way easier to do this ?
Thank you for your help !
Depending on how the passwords are hashed while new users are created, you could just replace the user's hash with another one :
Go in database and copy the passwordHash of the user you would like to log with
Store this hash somewhere (you will need it to revert the change)
Copy the hash of another user (a user for whom you know the password)
Paste the hash of this user in place of the hash of the user you want to log in
If the hashs are generated the same way, you will be able to log with the other user's password.
Then when you are done, revert the changes in database.
Hope it helps.
Passwords in django are one way encrypted so it cannot be decrypted hence there is no way of finding the actual password of any user from the user table in database.
If you want to login on another user's behalf without having to know their password then you can write some complicated code to achieve that, or use a third party package.
django-hijack looks like a good one. Its docs has good explaination on how to use it so I am not going to go through that here. If you don't like this package you can see the list of packages for this purpose here on djangopackages and choose the one you like.
django-hijack usage:
Complete the Installation and After Installation steps first.
Make a post request to /hijack/<user_id> where <user_id> is the column id of table user. If you have not updated the field id on model user it will be 1, 2, 3, .... So the url will be similar to /hijack/1/
Make a post request to /hijack/username/<username> if you want to hijack by username. eg. /hijack/username/awesome_username/
Make a post request to /hijack/email/<user email>/ if you want to hijack by email of user. eg. /hijack/email/awesome#email.com/
After making a post request to one of these urls as superuser you will be redirected to the specified HIJACK_LOGIN_REDIRECT_URL in settings.py
I am not sure if django-hijack will work on python 3 or newer versions of Django. If it doesn't works try out django-impersonate which provides the same functionality in a similar way and officially supports python 3.6+ and django 1.11+

Django one user across multiple django sites

I have a case where I have three sites A, B, C . A is more of administrative site, B and C are consumer facing applications. Each of them have their own separate databases. What I want to do now is as follows:
I want all three A, B, C to be connected in such a way that when a new user registers at B and when registration is successful, the same user should be able to login into A or C. If there is any change in users details they reflect on A and C as well.
What I thought as solution till now is:
When a user registers at B, I can replicate user on A and C with same credentials, so this would allow user to login into any of the three sites.
I was also pondering upon idea of having a central database to store django auth models, but I am not sure in that case how authentication would work.
I need suggestion as to how this can be done. Also, does this qualify to be called as SSO?
In case if someone finds question to be misleading or vague or inappropriate do mention as to why before downvoting
There are two solutions to your problem:
Use routing to multiple databases. Django supports different databases for different models (more info on # https://docs.djangoproject.com/en/1.8/topics/db/multi-db/). So you could route all queries for your auth models to the authentication database. Django documentation already provides such a configuration # https://docs.djangoproject.com/en/1.8/topics/db/multi-db/#an-example. I've not tested this configuration however this will have the problem of not being able to use foreign keys to your user model (since it will be stored in a different database). To resolve this, you could have a UserProxy model (or something similarly named) in all your projects that will just keep the username of the User so you'll be able to create foreign key relations from your models to the UserProxy. Any user data that would need to be retrieved would be forwarded to the correct User object by the UserProxy.
Use django authentication. Django can be configured to use a number of different authentication methods (check the docs # https://docs.djangoproject.com/en/1.8/topics/auth/customizing/). For your sites B and C you can configure an authentication backend that uses the database of the administrative site (A) to login. I am using this method succesfully - here how you could do it:
class RegUsrBackend(django.contrib.auth.backends.ModelBackend):
def authenticate(self, username=None, password=None):
try:
conn = django.db.connections['users']
cursor = conn.cursor()
cursor.execute("select pn.password, pn.username, au.first_name, au.last_name from auth_user au where au.username = %s ", [username])
row = cursor.fetchone()
if row and check_password(password, row[0]):
user, created = get_user_model().objects.get_or_create(username = row[1] )
if user.first_name != row[2] or user.last_name != row[3] :
user.first_name = row[2]
user.last_name = row[3]
user.set_unusable_password()
user.save()
return user
except:
return None
As you can see, here I've also configured a different users database (named users) and I am issuing a raw query to this database to get the user with the passed username and its password hash. After that, I check if the user exists and has the correct password (using the check_password) and if everything checks, I use get_or_create to either retrieve or create the local instance (i.e the instance of the user in the application's database) of that user. Finally, before returning the user I check to see if there's a change in his first or last name to the administrative application and update them in the local one.
Finally, you need to put this backend in the in the AUTHENTICATION_BACKENDS settings of the applications using it.
Using this method you won't have to use any UserProxy to support foreign keys (since there will exist a local User model) models but I feel that it is a more hackish method than the first one. Also, if the details of a user has been changed in the administrative database the details in the others will be updated when he logs in. Also you could change your backend to something even more exotic, for example instead of querying the database you could create a REST api in your administrative backend and use this to login to the other applications.
Finally, to answer your question about SSO, what I've described above is not SSO. SSO means that when a user logs in to a site he won't have to log in again to the others becase a single session key is kept and shared in all these sites. A common solution for SSO is to use CAS (http://jasig.github.io/cas/4.1.x/index.html) however I don't have good experience with it (and you'll need to have another, java based server to host CAS).

Django Rest Framework

I have a method that grabs POST data that is formated in json format like this
[{"UserName": "alexgv", "Password": "secretpassword"}]
Here is the method
def Login(request, *args):
data = request.DATA
return Response(data)
"""
try:
m = User.objects.get(UserName=request.DATA['UserName'])
if m.password == request.DATA['Password']:
request.session['member_id'] = m.id
return HttpResponse("Testing")
except User.DoesNotExist:
return HttpResponse("Your username and password didn't match.")
"""
I want to be able to take just one variable from that json POST. For example, maybe I just want to grab the UserName or Password. How would I do that? I have tried a variety of things but cant seem to get it to work, and I dont want to use request.POST.get because then that means I would have to send POST variables. BTW I am using this http://django-rest-framework.org/. I have read through the docs but cant seem to find anything in there. Any help is appreciated. What it returns right now is everything.
Like so...
username = request.DATA['UserName']
Incidentally, you probably shouldn't be writing session based API login views yourself as it's easy to do wrong.
For APIs that provide AJAX style functionality the you have two good options:
Login using a standard Django login, performed by the user, not performed by the API client.
Use a credentials based authentication scheme, rather than session based, and perform the login using AJAX. For example the Djoser third party package is a great library including token-based login and other similar views... https://github.com/sunscrapers/djoser
Update Also discovered https://github.com/JamesRitchie/django-rest-framework-sav which might be worth a look for AJAX session based authentication.

How to show user a form to set username when using django with django-social-auth

I've been using django-social-auth (https://github.com/omab/django-social-auth), with some success - logging in from Twitter and Facebook have been set up without difficulty. However, when I log in from some OpenID providers, I am faced with an empty variable for the username, and the social-auth app allows this (despite me having set SOCIAL_AUTH_DEFAULT_USERNAME).
Whilst if SOCIAL_AUTH_DEFAULT_USERNAME worked properly that might be an interim solution, ideally I'd rather that it was either set automatically from the openID provider. Basically, I'm left with two possible solutions:
1) Make a custom method to extract some of the extra data sent from the openID provider to set the username from that.
2) Force the user to set a username when they first login.
Whilst (2) is less elegant, it ensures that a username has been inserted each time, and also obviates the need to have some postpocessing of the automatic information which may not be in a suitable format for the username.
My question amounts to, how can I go about implementing either of the above! Complete answers are not necessary, but pointers to a method would be much appreciated!
The alternative is to play with django-socialregistration and to see whether that makes life easier!
J
1 is probably the way to go. You can override the get_user_details method of the Backend class for the social providers you're having trouble with and pull the username from there.
It'd look something like this, for example for Facebook:
from social_auth.backends.facebook import FacebookBackend
class CustomFacebookBackend(FacebookBackend):
name = 'facebook'
def get_user_details(self, response):
"""Return user details from Facebook account"""
return {'username': response['name'],
'email': response.get('email', ''),
'first_name': response.get('first_name', ''),
'last_name': response.get('last_name', '')}
The "response" variable is the deserialized data returned from the request to the OAuth provider for the user details, so you can pull whatever you need from that, or inject your own values here. social-auth will take whatever you stick in "username" and use that as the username for the new account.
If you want more control, you can try overriding the backend's "username" method as well.
When overriding the backend, don't forget to add it to AUTHENTICATION_BACKENDS in settings.py. Also, I'm not sure how this works exactly, but I think you need to add something like this to the file with your custom backend in order to get social-auth to link your backend in properly:
BACKENDS = {
'facebook': CustomFacebookAuth,
}

Categories

Resources