trying to delete inactive users in django - python

I am writing a site that sends confirmation emails on signup. Until the user clicks the link, the is_active field is set to false. How can I automatically delete any users who do not click the activation link within a certain period of time? Is this even a viable option or is there a better way to work around users who never confirmed their accounts?
Thanks for any help!

I had the same problem, what i did was set a celery task that checks for user that hasn't activated their account after a specified period...
your task.py should be like this
#shared_task(name="delete_unactivated_users")
def delete_unactivated_users(self, *args, **kwargs):
User = get_user_model() # Get User model
inactive_users = User.objects.filter(
date_joined__lt=timezone.now() - timezone.timedelta(seconds=86400),
) # Queryset to get users that have created an account but didn't activate them in a day.
for user in inactive_users:
# Optional line to log deleted users to a file
deleted_users_log(user)
user.delete()
function to write to file
def deleted_users_log(user):
f = open('/deleted_user_log.txt', 'a')
opened_file = File(f)
opened_file.write(f"Unactivated user {user.username} with firstName: {user.first_name}, lastName: {user.last_name}, and email: {user.email} have been flushed down the drain at: {datetime.datetime.now().time()}.\n")
opened_file.close
f.close
I am assuming that your token will expire after a day (24hrs)(86400 seconds)
Reference

Related

Django: phone verification for inactive account

I'd like to implement phone verification with pyotp in my view class-based Django (2.5) project.
After new users sign up (specifying name, phone, e-mail and password) in RegisterView, they should be redirected to GetAccessCodeView with an input field for verification code and a hidden field with a secure token. For generating and sending the code and the token I have to pass there a newly created user instanse from RegisterView to GetAccessCodeView.
How can I do that? Currently newly created users have is_active field set to False (it should become True after code succesful verification), thus cannot be authorized by default, so without changing login procedure, it is impossible to use request.user directly. But if I let inactive users to log in, then all the login_required views will let unconfirmed users to open corresponding pages. Should I write is_active check for each view manually or maybe Django has some ready stuff like 'login_and_active_required'? Or maybe there is some different solution?

How to set user name in session and get the user name in other view methods

I'm new to Python and Django.
I'm using PHP form for login mechanism and once the login is successful, I'm sending the user name to Django application to carry on with other operations but I'm stuck in finding a best way to store the user name in session so that it can be used in other view methods
Below are some of my tries which failed:
1) Storing the user name in a global variable and access throughout the application but when another user login from their machine, user name gets changed in my machine.
2) Storing the user name in Django DB session but another user logs in with different user, it's showing the old user only.
3) I tried storing in the cache but still, it's not working.
DB session handler: It works fine when I access in sessionHandler method but it throws key error when i try to access in login method.
def sessionHandler(request):
userName = request.GET.get('uname')
request.session['user'] = userName
print("current logged in user: " + request.session['user'])
return JsonResponse({'Response': 'success'})
def login(request):
uname = request.session.get('user')
UserName = {"Name": uname}
return render(request, 'login/login.html', UserName)
My use case is that any user who logs in their own machine and with own user name and password should be saved in the session data. Please help me to move further with this.
Thanks in advance !!!

Expire the user activation link and remove from database in django

I have an user signup, where the user's account is activated after link with a token send to their email address is clicked. I want to expire the link and delete their data from the database if the specific link is not clicked within 24 hours.
I have read it in one place, that the link is expired after 48 hours, is that correct?
Here is my question -
How can I automatically remove those users who do not click on the activation link with in 24 hours?
(So far I can do that by going to admin panel and by checking email is confirmed or not)
Here is my activate function,
def activate(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, ObjectDoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
user.is_active = True
user.email_confirmed = True
user.save()
login(request, user)
return redirect('home')
else:
return render(request, 'user/account_activation_invalid.html')
This is my tokens.py to create token,
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils import six
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (
six.text_type(user.pk) +
six.text_type(timestamp) +
six.text_type(user.email_confirmed)
)
account_activation_token = AccountActivationTokenGenerator()
What should I change to achieve that?
The default expiration time of the token is 3 days (72 hours).
You don't need to save the token in database. The token already contains the timestamp of creation time. So all you need to do is override the check_token method in your custom class and check if the timestamp is 24 hours old or not.
Most of the code can be copied verbatim from the the original method. See the source code on github.
All you have to do is change line 49
Sample code:
class AccountActivationTokenGenerator(...):
...
def check_token(self, user, token):
...
if (self._num_days(self._today()) - ts) > 1: # 1 day = 24 hours
return False
...
UPDATE:
To delete unverified users after 24 hours, you can create a cron job which runs every 24 hours and checks your database for unverified users and deletes them if they are more than 24 hours old.
Here's an answer which gives an outline of the process: Django - Set Up A Scheduled Job?. For creating a cron job, see your operating system's documentation.
Another method of adding cron jobs is by using django apps such as django-cron and django-crontab. They are specifically created for making this task easier, but the general principle stays the same as described in the linked answer.

Admin(only) registration of users, Flask-Security

I'm currently building a login for a webapp using Flask-Security (which includes Flask-WTForms, Flask-SQLalchemy and Flask-Login). I've been able to fairly painlessly set up the majority of my login flow, including forgotten password; however I want to make it so that the only way users can be registered is through a page only accessible to the admins. I've managed to configure Roles (Admin, User) and set up the following view:
#app.route('/adminregister')
#roles_accepted('admin')
def adminregister():
return render_template('*')
This creates the portal that is successfully limited to accounts with an Admin role. I'm unsure how to proceed for here however, as Flask-security has no built in means to enable what I'm trying to do.
I've overridden RegisterForm already to enforce password rules through a regexp:
# fixed register form
class ExtendedRegisterForm(RegisterForm):
password = TextField('Password', [validators.Required(), validators.Regexp(r'(?=.*?[0-9])(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[$-/:-?{-~!"^_`\[\]])')])
Basically I want a form, located at /adminregister, that when visited by an admin allows for the entry of an email address, at which point first the user is created in the database with a random and secure password, and then a similar process to a forgotten password happens and a 1 time password code is created to reset the password.
Useful things I've looked at:
Within flask-security/views.py there is the forgotten passsword code:
def forgot_password():
"""View function that handles a forgotten password request."""
form_class = _security.forgot_password_form
if request.json:
form = form_class(MultiDict(request.json))
else:
form = form_class()
if form.validate_on_submit():
send_reset_password_instructions(form.user)
if request.json is None:
do_flash(*get_message('PASSWORD_RESET_REQUEST', email=form.user.email))
if request.json:
return _render_json(form, include_user=False)
return _security.render_template(config_value('FORGOT_PASSWORD_TEMPLATE'),
forgot_password_form=form,
**_ctx('forgot_password'))
Within flask_security/registerable.py there is the code for register_user
def register_user(**kwargs):
confirmation_link, token = None, None
kwargs['password'] = encrypt_password(kwargs['password'])
user = _datastore.create_user(**kwargs)
_datastore.commit()
if _security.confirmable:
confirmation_link, token = generate_confirmation_link(user)
do_flash(*get_message('CONFIRM_REGISTRATION', email=user.email))
user_registered.send(app._get_current_object(),
user=user, confirm_token=token)
if config_value('SEND_REGISTER_EMAIL'):
send_mail(config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome',
user=user, confirmation_link=confirmation_link)
return user
I want to somehow combine these two, so that upon submission of a form with the sole field "Email" at '/adminregister' the email is added with a secure, random password in the database and the email address is sent an email with a link to change there password (and ideally a message explaining). I'm not even sure where I would add such code, as there is nothing to specifically override, especially as I can't find a way to override RegisterForm to have FEWER fields and the same functionality.
The structure of my code is in line with the flask-security documentation's quickstart.
Thank you in advance for any guidance you can offer.
I ended up using a work around as follows:
I enabled registration but limited registration view to users with an admin role.
I used del form.password in views -> register to no longer send the form with a password field.
I did the following in .registerable, generating a random password to fill the table.
kwargs['password'] = encrypt_password(os.urandom(24))
Upon admin entry of an email in the registration form, I had confimable enabled. This means the user would immediatly get an email to confirm their account and explaining they'd been registered. Upon confirmation they are redirected to the forgotten password page and asked to change their password (which is limited based on security).
If anyone comes up with a more direct way I'd appreciate it. I'm leaving this here in case anyone has the same problem.
The register process creates a signal with blinker that you can access like this:
from flask.ext.security.signals import user_registered
#user_registered.connect_via(app)
def user_registered_sighandler(app, user, confirm_token):
user_datastore.deactivate_user(user)
db.session.commit()
Which will deactivate any newly registered users.
I know this is an ancient question, but I think I have an elegant answer.
first import register_user
from flask_security.registerable import register_user
Then since you do not want just anyone to register ensure registerable is disabled (though disabled is the default so you can omit this) and since you want to send confirmation email, enable confirmable, and changeable for users to change their paswords
app.config['SECURITY_CONFIRMABLE'] = True
app.config['SECURITY_REGISTERABLE'] = False
app.config['SECURITY_RECOVERABLE'] = True
Then, you can do your create your user registration view and decorate it with role required. I have used my own custom registration form so I have had to go an extra mile to check if user already exists and return an error accourdingly
#app.route('/admin/create/user', methods=['GET', 'POST'])
#roles_required('admin')
def admin_create_user():
form = RegistrationForm(request.form)
if request.method == 'POST' and form.validate_on_submit():
email = form.email.data
password = form.password.data
user_exists = session.query(User).filter_by(email=email).first()
if user_exists:
form.email.errors.append(email + ' is already associated with another user')
form.email.data = email
email = ''
return render_template('create-user.html', form = form)
else:
register_user(
email=email,
password = password)
flash('User added successfully')
return render_template('create-user.html', form = form)
Also see flask-security - admin create user, force user to choose password
Here's another solution I found after poking through flask-security-too. I made an admin create user form, and simply add the following code after creating the user in the database:
from flask_security.recoverable import send_reset_password_instructions
# my code is maintains self.created_id after creating the user record
# this is due to some complex class involved which handles my crudapi stuff
# your code may vary
user = User.query.filter_by(id=self.created_id).one()
send_reset_password_instructions(user)

Saving user settings when using federated login

I need to save settings for each user on my application.
I tried to use the user as a parent of my settings object, but I cannot do this since users do not have keys.
I then created an instance in my settings object that has a reference to user property, but in the docs it says UserProperty is unstable if the user changes their email address.
I then decided to save the user_id in a StringProperty() but if the user logs in with OpenId, the user_id element is None.
Is there a way to tie the user settings to the user object that works for both google accounts and open_id accounts?
Couldn't you add a wrapper class around the google account / open_id account, so you can use the parent relationship? Something like the following:
UserWrapper(db.Model):
user = db.UserProperty()
UserSettings(db.Model):
...
# New User
user = users.get_current_user()
new_user_settings = UserSettings(...)
new_user_wrapper = UserWrapper(key=user.nickname(),
parent=new_user_settings,
user=user)
# Login Existing User
user = users.get_current_user()
user_wrapper = UserWrapper.get_by_key_name(user.nickname())
user_settings = user_wrapper.parent()
If the user wants to change their email address, look up the UserSettings with the old email, delete the associated UserWrapper, create a new UserWrapper for the new email address and associate with the old UserSettings.
Note I've made the UserSettings a parent of UserWrapper, in case associating multiple email addresses with the same account may be something of interest.

Categories

Resources