Forced password reset in django-admin

1,062 views
Skip to first unread message

benjaoming

unread,
Jun 29, 2011, 7:36:45 AM6/29/11
to Django developers
Dear all,

As a maintainer of many Django sites, I would often like to see a very
small feature implemented, that could make life a lot easier for me:
To force my users to set their own password.

I know this could lead to a long debate about password strength, SSL,
password reminders, secret answers etc... but for me, the most urgent
problem is that people have to be reminded manually about their
passwords, and I have to reset them pretty often (or find copies of
emails that i've sent).

I always tell people to click the Change password link. But they
don't!

So the problem seems to be that ordinary users do not change their
password, forget it, delete the original email (or are too lazy to
find it), and then some poor administrator has to make another one.

Forcing people to reset their password upon login is a common feature
in Google Apps btw.

What are people's opinions? Should this be a core feature?

Django-love,
Benjamin

Luke Plant

unread,
Jun 29, 2011, 8:07:45 AM6/29/11
to django-d...@googlegroups.com
On 29/06/11 12:36, benjaoming wrote:
> Dear all,
>
> As a maintainer of many Django sites, I would often like to see a very
> small feature implemented, that could make life a lot easier for me:
> To force my users to set their own password.

First, to me, this is not obviously a 'very small feature'.

Second, is there any reason it has to be in core? Ideally it could be
implemented in 3rd party library. At that point it would be useful to
you, and we could assess whether it is general enough to be in core.

If it can't be implemented as a 3rd party solution, then it requires
some invasive change to Django itself, which means it is very definitely
not a small feature, and would require much more discussion about how it
would be configured etc. It is much less likely to be accepted if it is
being forced on everyone, or if it requires some new setting to control it.

Third, the need for things like checking password strength means there
probably isn't going to a one-size-fits-all solution. I think this
should be a 3rd party library.

Luke

--
"If we could just get everyone to close their eyes and visualise
world peace for an hour, imagine how serene and quiet it would be
until the looting started" -- Anon

Luke Plant || http://lukeplant.me.uk/

benjaoming

unread,
Jun 29, 2011, 9:27:02 AM6/29/11
to Django developers
> > As a maintainer of many Django sites, I would often like to see a very
> > small feature implemented, that could make life a lot easier for me:
> > To force my users to set their own password.
>
> First, to me, this is not obviously a 'very small feature'.
>
> Second, is there any reason it has to be in core? Ideally it could be
> implemented in 3rd party library. At that point it would be useful to
> you, and we could assess whether it is general enough to be in core.

I'm sorry if "core" was the wrong use... "contrib.auth" is what I
meant.

How so do you find it an invasive change? I think it could be solved
easily, even without being backwards incompatible.

This type of login behavior is standard in Google Apps, which is why I
find it not to be something I've made up just for my own needs!

Here's a way to do it:

When a user has never been logged in, User.last_login is the same as
User.date_joined -- so we actually do not need a new model field! We
can rely on this behavior as a sort of "intended" logical derivation
from the fact that they are equal :) Furthermore, I would propose of
course to make the behavior configurable and turned off by default.

The whole conditional redirect could easily be put in
django.contrib.auth.views - all we need to do is put 4 lines of code
on each side of auth_login(request, user) in the login(...) view -
like 'dis:

def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME,
authentication_form=AuthenticationForm,
current_app=None, extra_context=None):
"""
Displays the login form and handles the login action.
"""

(...)

if form.is_valid():

(...)

# The form is valid... and now I would propose inserting
something like:
user = form.get_user()
force_password_reset = False
if settings.AUTH_FORCE_USER_PASSWORD_RESET and
user.last_login == user.date_joined:
force_password_reset = True

# Okay, security checks complete. Log the user in.
auth_login(request, user)

if force_password_reset:
# Manipulate last_login so that the user will be
consistently redirected on each login, until the password is reset.
user.last_login = user.date_joined
user.save()
redirect_to =
reverse('django.contrib.auth.views.password_reset'))

Daniel Moisset

unread,
Jun 29, 2011, 12:01:38 PM6/29/11
to django-d...@googlegroups.com
On Wed, Jun 29, 2011 at 8:36 AM, benjaoming <benja...@gmail.com> wrote:
>
> Dear all,
>
> As a maintainer of many Django sites, I would often like to see a very
> small feature implemented, that could make life a lot easier for me:
> To force my users to set their own password.
>
> I know this could lead to a long debate about password strength, SSL,
> password reminders, secret answers etc... but for me, the most urgent
> problem is that people have to be reminded manually about their
> passwords, and I have to reset them pretty often (or find copies of
> emails that i've sent).

django.auth already provides a password reset mechanism, where users
can get a password reset link.

So, woudn't this work for you?:

1) Create users with a large random password. Destroy every copy of the password
2) Use the reset password feature with their email to force it to set
an initial value

That way you never send them actual passwords, and the only passwords
they can use are the ones they set.

all of this works with no additional support needed from django

D.

Shawn Milochik

unread,
Jun 29, 2011, 12:21:24 PM6/29/11
to django-d...@googlegroups.com
On 06/29/2011 12:01 PM, Daniel Moisset wrote:
> django.auth already provides a password reset mechanism, where users
> can get a password reset link.
>
> So, woudn't this work for you?:
>
> 1) Create users with a large random password. Destroy every copy of the password
> 2) Use the reset password feature with their email to force it to set
> an initial value
>
> That way you never send them actual passwords, and the only passwords
> they can use are the ones they set.
>
> all of this works with no additional support needed from django
>
> D.
>

That would work, and I'd recommend using the provided
set_unusable_password method instead of setting a random password for
additional security.


TiNo

unread,
Jun 29, 2011, 5:48:24 PM6/29/11
to django-d...@googlegroups.com
Hi,

On Wed, Jun 29, 2011 at 18:01, Daniel Moisset <dmoi...@machinalis.com> wrote:
django.auth already provides a password reset mechanism, where users
can get a password reset link.

So, woudn't this work for you?:

1) Create users with a large random password. Destroy every copy of the password
2) Use the reset password feature with their email to force it to set
an initial value

That way you never send them actual passwords, and the only passwords
they can use are the ones they set.

all of this works with no additional support needed from django

This definitely does work, however, shouldn't this be a bit more easy? To be honest, when would you prefer creating a user for someone with a password that *you* made up, instead of having the person using a password *they* like best? Couldn't we create a button that sends an 'new account created' email, or creates a link to 'activate account'/'pick your password'? This is one of the biggest hurdles I usually have with the admin and giving other people accounts, let alone having somebody not used to django be the superuser or main admin.

Tino

ps. This function would then use the set_unusable_password method suggested by Shawn of course.

Luke Plant

unread,
Jun 29, 2011, 7:19:00 PM6/29/11
to django-d...@googlegroups.com
On 29/06/11 14:27, benjaoming wrote:

> How so do you find it an invasive change? I think it could be solved
> easily, even without being backwards incompatible.

I didn't say it was invasive. I said that if it's *not* invasive then it
can be implemented as a 3rd party library. If that was done, we could
assess and see if it was general enough to be used in contrib.

> This type of login behavior is standard in Google Apps, which is why I
> find it not to be something I've made up just for my own needs!
>
> Here's a way to do it:
>
> When a user has never been logged in, User.last_login is the same as
> User.date_joined -- so we actually do not need a new model field! We
> can rely on this behavior as a sort of "intended" logical derivation
> from the fact that they are equal :) Furthermore, I would propose of
> course to make the behavior configurable and turned off by default.
>
> The whole conditional redirect could easily be put in
> django.contrib.auth.views - all we need to do is put 4 lines of code
> on each side of auth_login(request, user) in the login(...) view -
> like 'dis:

What you have suggested here **is** an invasive change, because it
requires changes to existing code paths. A setting is not a good way to
make something configurable here. A keyword argument here *would* be an
acceptable solution.

However, I don't think it would be that useful, because if it was off by
default, and controlled by a keyword argument, then the admin login view
wouldn't use it. I think this needs more thought.

Again, I should mention that features like this get implemented by the
community. If you need it, the way to do it is to build it, and then
persuade us to use it - not to try to persuade *us* to build it :-)
The logic can be implemented as 3rd party view than people can plug into
their projects.

> def login(request, template_name='registration/login.html',
> redirect_field_name=REDIRECT_FIELD_NAME,
> authentication_form=AuthenticationForm,
> current_app=None, extra_context=None):
> """
> Displays the login form and handles the login action.
> """
>
> (...)
>
> if form.is_valid():
>
> (...)
>
> # The form is valid... and now I would propose inserting
> something like:
> user = form.get_user()
> force_password_reset = False
> if settings.AUTH_FORCE_USER_PASSWORD_RESET and
> user.last_login == user.date_joined:
> force_password_reset = True
>
> # Okay, security checks complete. Log the user in.
> auth_login(request, user)
>
> if force_password_reset:
> # Manipulate last_login so that the user will be
> consistently redirected on each login, until the password is reset.
> user.last_login = user.date_joined
> user.save()
> redirect_to =
> reverse('django.contrib.auth.views.password_reset'))
>

BTW - this doesn't actually force the password reset - it logs them in
and then asks them to change their password. If they don't they are
still logged in.

benjaoming

unread,
Jun 30, 2011, 6:08:42 AM6/30/11
to Django developers
Hello again!

> What you have suggested here **is** an invasive change, because it
> requires changes to existing code paths.

I think the way to measure "invasive" is by means of backwards
compatibility. We do not change required arguments, return value or
model fields... The view normally responds with an
HttpResponseRedirect object, and that's also the case here.

> A setting is not a good way to
> make something configurable here. A keyword argument here *would* be an
> acceptable solution.

You're right. Keywords argument is a good idea!

> However, I don't think it would be that useful, because if it was off by
> default, and controlled by a keyword argument, then the admin login view
> wouldn't use it. I think this needs more thought.

True! Actually this speaks against the use of a keyword argument as
sole option.

Keyword option: Local per-view behavior
Setting variable: Project-wide behavior used when there is no kwarg
present

> Again, I should mention that features like this get implemented by the
> community. If you need it, the way to do it is to build it, and then
> persuade us to use it - not to try to persuade *us* to build it :-)

I'm actually trying to persuade myself to build it in case there's
enough support+discussion for it.

> BTW - this doesn't actually force the password reset - it logs them in
> and then asks them to change their password. If they don't they are
> still logged in.

Yup, that needs more thought!

all the best,
Benjamin

Wim Feijen

unread,
Jul 2, 2011, 4:18:36 PM7/2/11
to Django developers
Hi Bejamin,

Sounds like a cool feature: probably when you build such a thing, it
will be used by dozens (or thousands). I noticed that I am making a
lot of use of custom registration processes. Most of the time, I use
django-registration as a solid basis, are you familiar with that?

Best regards,

Wim
Reply all
Reply to author
Forward
0 new messages