check_password in contrib auth tries to update the database

164 views
Skip to first unread message

suhrid...@proteus-tech.com

unread,
May 30, 2014, 9:58:38 AM5/30/14
to django...@googlegroups.com
Hi,

I am getting this error 
cannot execute UPDATE in a read-only transaction
from a method in auth module that supposedly checks for password.

check_password method of AbstractBaseUser in django.contrib.auth.models tries to update the database.
This causes problems when this code executes on a read-only slave database.

Here is the source code of the method:

    def check_password(self, raw_password):
        """
        Returns a boolean of whether the raw_password was correct. Handles
        hashing formats behind the scenes.
        """
        def setter(raw_password):
            self.set_password(raw_password)
            self.save(update_fields=["password"])
        return check_password(raw_password, self.password, setter)


Has anyone come across this issue before? 

--
Suhrid

Erik Romijn

unread,
May 31, 2014, 4:54:51 AM5/31/14
to django...@googlegroups.com
Hello Suhrid,

On 30 May 2014, at 15:58, suhrid...@proteus-tech.com wrote:
> check_password method of AbstractBaseUser in django.contrib.auth.models tries to update the database.
> This causes problems when this code executes on a read-only slave database.
>
> def check_password(self, raw_password):
> def setter(raw_password):
> self.set_password(raw_password)
> self.save(update_fields=["password"])
> return check_password(raw_password, self.password, setter)

Yes, this is a feature, which enables upgrading of password hashing. When Django encounters a password that is hashed with an older hash, the setter will be called, which will save the password with the current preferred hash. This can only be done when the raw password is known, which can only happen while Django is checking the password.

I can see how this is an issue in your scenario. A solution I can come up with is to extend this user model[1], where you override only the model's check_password method. The setter parameter to check_password is optional, and if absent it will simply not upgrade passwords. However, the downside of this is of course, that passwords will not be upgraded if we add better hashers in the future.

[1] https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model

cheers,
Erik

suhrid...@proteus-tech.com

unread,
Jun 2, 2014, 1:50:55 AM6/2/14
to django...@googlegroups.com
Hi Erik,

Thanks for the explanation. This looks like something I could try.

Another approach that I found recently was to use database routers[1].
This would not cause problems with password upgrades, but we would need to change some database permissions.

[1] https://docs.djangoproject.com/en/dev/topics/db/multi-db/#using-routers

--
Suhrid
Reply all
Reply to author
Forward
0 new messages