Adding Password Requirements to Django's Admin Interface - django

The "Overview" section of the "User authentication in Django" documentation says that the authentication system in Django doesn't provide password strength checking. I wrote a form class that adds some basic password requirements such as minimum character length. I'm trying to implement it in Django's admin interface. As far as I know, there are three places I will need to implement my password requirements:
Creating a user: /admin/auth/user/add/
Changing a user's password: /admin/auth/user/1/password/
Changing my own password: /admin/password_change/
I can take care of the first two by subclassing UserAdmin and specifying add_form and change_password_form:
https://github.com/django/django/blob/1.8.2/django/contrib/auth/admin.py#L58-L59
How can I get the third one (changing my own password) to use my password requirements? The code is a little above me:
https://github.com/django/django/blob/1.8.2/django/contrib/admin/sites.py#L314

Obviously by sub-classing and using your own AdminSite
# my_admin.py
class MyAdminSite(AdminSite):
def password_change(self, request, extra_context=None):
...
site = MyAdminSite()
You can disable autodiscovery for default admin site, by using AdminConfig for INSTALLED_APPS settings (django 1.8+ only)
INSTALLED_APPS = (
'django.contrib.admin.apps.AdminConfig',
...
)
And then you have to manually register required models to your new admin site.

Related

Forcing unique email address during registration with Django

Is there a simple way to force unique email address's during registration with website built with Django?
I've seen some "addons?" like HMAC, but it seems a bit too complicated for what I am trying to achieve.
Also, would it be possible to accept registration only from a list of domains? (such as only emails from "#google.com")
I had the same problem and solved it by extending the AbstractUser class to my own class MyUser and changing the defaults.
Then by making the this class MyUser as a default model class for all my users I could apply this property(unique E-Mail) to all my users on my web app.
Create an app myuser. There in models.py:
from django.contrib.auth.models import AbstractUser
#create your own user class.
class MyUser(AbstractUser):
def __init__(self, *args, **kwargs):
self._meta.get_field('email').blank = False
self._meta.get_field('email')._unique = True
super(MyUser, self).__init__(*args, **kwargs)
#Changed the defaults above.
#Give any additional field you want to associate your user with.
NOTE: AbstractUser already has all the basic fields you would want a User Model to have. For example: username, password, email etc. Check all of them here.
The last thing you would want to do is add the following in your setting.py
AUTH_USER_MODEL = 'myuser.MyUser'
This will make sure that the default user is associated with your web app is the extended(modified) MyUser class. This will provide you with all the basic functionalities that django provides for a User.
login
logout
in your views: you can get user instance in: request.user
etc.
I would like to suggest that you may need some additional code(in forms.py and views.py) to create a user through this type of class. I hope you will manage that. This should be enough to guide you in the right direction.
Maybe a library would have helped but since you needed an authentication for emails' domains as well, I think this should do the trick. In my humble opinion, you can't always depend on the 3rd party libraries for every other functionality.
Lastly, as you asked to authenticate a user coming only from a domain like #gmail.com or #outlook.com, a simple check in your django forms' clean method would do the trick. I hope you know how to handle django forms. If not, then you can learn about them in the official docs. They are an essential part of Django.
You can check the E-Mail with this logic:
email = self.cleaned_data['email']
email_source = email.split('#')[-1]
#email_source will now have values like: gmail.com, outlook.com etc
#you can now validate email_source now like:
permitted_sources = ['gmail.com' , 'outlook.com' , ]
if email_source in permitted_sources:
return cleaned_data
else:
raise forms.ValidationError('Error Message')
#Note: This logic should be kept in your clean method.
I hope this guides you. Thanks.

django - Extending `auth.models.User` and usering login, logout

If I create a CustomUser model which inherits from django.contrib.auth.models.User, like so:
in models.py
class CustomUser(django.contrib.auth.models.User):
customfield = TextField()
...
Should I still be able to use
django.contrib.auth.{authenticate, login, logout} in the normal way? Do I have to make some additional configuration change? I know these methods only work on User objects, but technically my CustomUser is-a User.
Currently, authenticate(username=u, password=p) is always returning None, even with valid credentials.
Since Django 1.5 (officially but it doesn't worked for me) and "stable" in 1.6 there is a functionality to extend the User model in a clean way.
At first:
-> Take care that you load the User model only via:
from django.contrib.auth import get_user_model
User = get_user_model()
-> Once you have built the database theres no easy way to change the User model. The database relations will break and Django / South isn't able to fix it.
-> third party modules have to be compatible with that new layout and refer in it's models to "get_user_model()", too.
You have to add some Code for the admin to respect your new model:
See: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model
To Override the model you need to inherit from AbstractBaseUser:
from django.contrib.auth.models import AbstractBaseUser
class MyUser(AbstractBaseUser):
...
date_of_birth = models.DateField()
height = models.FloatField()
...
REQUIRED_FIELDS = ['date_of_birth', 'height']
AbstractBaseUser provides you all attributes of the default user model. So you don't have to take care of email, username, first_name, last_name, password etc.
More info about overriding the user model: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#django.contrib.auth.models.CustomUser
In your settings link your new model:
AUTH_USER_MODEL = 'customauth.MyUser'
Please read the whole documentation of customizing the user model, there are some interesting hints for overriding the default manager, admin forms etc. Just remember that bigger changes in an existing project can be a big pain.
A short overview:
- Extend models.AbstractUser
- Set AUTH_USER_MODEL in settings.py
All details can be found here: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model

How do staff users edit their profiles in Django admin?

I'm new to Django. I'm working on a website that will only contain users as staff users. I'm the administrator. My plan is to register a user and give him his username and password to login; I want him only to be able to edit his profile to add more information, not to change existing attributes. How can I do that?
The easiest way would probably be to create a appropriate ModelForm and a corresponding view, that checks that the instance the user want's to update is the own instance.
I you want to integrate this directly in the admin backend, you can also do this. The most djangoish way would propably be to create a own ModelAdmin class for the User that has the right methods overridden (see the methods startinghere). I think you should start with overriding has_change_permission where you can check if the object the user tries to edit, is his own and return False otherwise.
To replace the standard User ModelAdmin you need to do a little fiddeling in the admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
class MyUserAdmin(UserAdmin):
# add the code here
# deregister the standard AdminModel and register the own one
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
(I'm using this code on a live site and it works create)

Using custom User admin breaks change password form in Django's admin

I'm using a custom User admin by:
class CustomUserAdmin(admin.ModelAdmin):
model = User
...
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
but when I try to change the password via the admin page I get a 404.
user object with primary key u'4/password' does not exist.
Reverting back to the default User admin works fine.
The default UserAdmin in django.contrib.auth.admin implements a lot of functionality, including the change password page.
Your CustomUserAdmin should subclass UserAdmin instead of admin.ModelAdmin, unless you want to reimplement that functionality yourself.
class CustomUserAdmin(UserAdmin):
# as an example, this custom user admin orders users by email address
ordering = ('email',)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
Also:
As per the docs, if you inherit from AbstractBaseUser you cannot use the default UserAdmin; or, put another way, you can but only a subset of the functionality will work - changing an existing password might work, but adding a new user will throw exceptions.

Django: how to store subdomain-based authentication usernames?

I need to create a subdomain based authentication system, like the one 37signals, freshbooks, codebase use. That is, each subdomain of my main application needs to have its own username namespace. I would like to keep as much as possible of the django authentication system.
What is a good way to store the username?
In particular, it should be possible for different users to have the same username as long as their account belongs to a different subdomain.
Some approaches I've considered, for which I can foresee shortcomings:
storing some prefix in the username field of the django auth user model.
extending the user model according to this.
customizing the source of auth to my needs
I have built this functionality for several sites in the past and have found that your first bullet point is the way to go.
It means that you don't have to make massive change to django auth. What I did was set up a custom authentication backend that abstracts away the way usernames are stored.
auth_backends.py
from django.contrib.auth.backends import ModelBackend
from Home.models import Account
class CustomUserModelBackend(ModelBackend):
def authenticate(self, subdomain, email, password):
try:
user = Account.objects.get(username=u'%s.%s' % (subdomain, email))
if user.check_password(password):
return user
except Account.DoesNotExist:
return None
def get_user(self, user_id):
try:
return Account.objects.get(pk=user_id)
except Account.DoesNotExist:
return None
For this particular project Account was the user model and it just inherited directly from User however you could replace Account with whatever you want.
You have to install the custom auth backend in your settings file:
AUTHENTICATION_BACKENDS = (
'auth_backends.CustomUserModelBackend',
'django.contrib.auth.backends.ModelBackend',
)
Then when you call authenticate you need to pass in the subdomain, email and password.
You can also add some other helper functions or model methods that help with making sure that only the user's actual username is displayed, but that is all pretty trivial.
I think this may be a good use case for using django.contrib.sites in combination with the second bullet item you mentioned. You could create a CustomUser model like so:
from django.contrib.sites.models import Site
class CustomUser(User):
"""User with app settings."""
sites = models.ManyToManyField(Site)
Then you could write a custom auth backend to check that the user can sign in to the current subdomain using the supplied credentials. This allows you to have one username for multiple sites (subdomains) without having to hack the internal auth app or store multiple usernames with custom prefixes.
EDIT: you can get the current site by using Site.objects.get_current() and then check to see if the current site is in the user's sites.
You can read more about the sites framework here: http://docs.djangoproject.com/en/dev/ref/contrib/sites/