I am trying to Custom authentication login with a legacy database. So far I still do not know how to do it. However, when I copy this example code, and try to write some sample code it can work. However, my legacy database is md5 encryption. I now am trying to change my sample code to md5 encryption.
I just
import hashlib
and #out
ReadOnlyPasswordHashField
and in
class UserChangeForm:
change the code
password = hashlib.md5()
admin.py
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
#from django.contrib.auth.forms import ReadOnlyPasswordHashField
import hashlib
from .models import MyUser
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = MyUser
fields = ('email', 'date_of_birth')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
#password = ReadOnlyPasswordHashField()
password = hashlib.md5()
class Meta:
model = MyUser
fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'date_of_birth', 'is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('date_of_birth',)}),
('Permissions', {'fields': ('is_admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'date_of_birth', 'password1', 'password2')}
),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
however,,when I recreate a user and check my db it is still shows pbkdf2 ??,,
Can any one tell me how to change to md5? Thank you very much!
It doesn't matter what you do in your view. The password will still be hashed according to the settings in PASSWORD_HASHERS. I'm not understanding at all why you need to downgrade to MD5...But if you really need to, you can change this setting in settings.py:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
]
Related
So to use email as username I override the build-in User model like this (inspired by Django source code)
models.py
class User(AbstractUser):
username = None
email = models.EmailField(unique=True)
objects = UserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
def __str__(self):
return self.email
admin.py
#admin.register(User)
class UserAdmin(admin.ModelAdmin):
fieldsets = (
(None, {"fields": ("email", "password")}),
(("Personal info"), {"fields": ("first_name", "last_name")}),
(
("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)
list_display = ("email", "is_active", "is_staff", "is_superuser")
list_filter = ("is_active", "is_staff", "is_superuser")
search_fields = ("email",)
ordering = ("email",)
filter_horizontal = ("groups", "user_permissions",)
But this is how it looks like when I go to Admin site to change a user:
Password is visible and not hashed and no link to change password form.
Comparing to what it looks like on a default Django project:
Password is not visible and there's a link to change password form
So clearly I'm missing something but I can't figure out what it is.
It's likely that the Django Admin look problem has to do with inheritance. More precisely, change the class to inherit from UserAdmin.
from django.contrib.auth.admin import UserAdmin as DefaultUserAdmin
class UserAdmin(DefaultUserAdmin):
Doing this will make it look closer to the default Django project.
The problem of the password not being encrypted is likely because it's not encrypted in the database. OP has this question and that's not a problem of the Django Admin.
Notes:
I also use AbstractUser when wanting to remove username.
Since this current question doesn't have enough info to answer the problem of the pwd being hashed, I'll leave that to OP's other question.
You are not able to see the password in hashed state because the password field is a CharField which renders it as normal text field. In Django's admin side there's a field called ReadOnlyPasswordHashField in django.contrib.auth.forms which renders the password field to be in hashed state with password change link.
Django's UserAdmin uses different form classes for user creation and updation.
form = UserChangeForm
add_form = UserCreationForm
change_password_form = AdminPasswordChangeForm
To edit user details UserAdmin uses form = UserChangeForm(source code) where the password field is set as ReadOnlyPasswordHashField(source code).
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=_(
"Raw passwords are not stored, so there is no way to see this "
"user’s password, but you can change the password using "
'this form.'
),
)
So, Just by inheriting from UserAdmin from django.contrib.auth.admin would make the password to be in hashed state with all the other essentials as seen in default admin site for users.
OR
you could simply import UserChangeForm from django.contrib.auth.forms and set form = UserChangeForm in custom UserAdmin
from django.contrib.auth.forms import UserChangeForm,AdminPasswordChangeForm
# code
#admin.register(User)
class UserAdmin(admin.ModelAdmin):
# code
form = UserChangeForm
change_password_form = AdminPasswordChangeForm
# code
Django Documentation clearly explains how to do this by Customizing authentication in Django
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from customauth.models import MyUser
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = MyUser
fields = ('email', 'date_of_birth')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
disabled password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = MyUser
fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'date_of_birth', 'is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('date_of_birth',)}),
('Permissions', {'fields': ('is_admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'date_of_birth', 'password1', 'password2'),
}),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
One way is to define a custom fieldset for the UserAdmin in admin.py.
fieldsets = (
(None, {"fields": ("username")}),
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
I have this in my models.py:
class UserManager(BaseUserManager):
def create_user(self, name, password=None):
"""
Creates and saves a User with the given name and password.
"""
if not name:
raise ValueError('A user must have a name.')
user = self.model()
user.set_password(password)
user.save(using=self._db)
return user
def create_staffuser(self, name, password):
"""
Creates and saves a staff user with the given name and password.
"""
user = self.create_user(
name,
password=password,
)
user.staff = True
user.save(using=self._db)
return user
def create_superuser(self, name, password, id):
"""
Creates and saves a superuser with the given name and password.
"""
user = self.create_user(
name,
id,
password=password,
)
user.staff = True
user.admin = True
user.save(using=self._db)
return user
The reason I added the field id in my create_superuser -method is that it is a required field in my model and I thought I need to include it also when running python manage.py createsuperuser. My problem is that I don't know how to customize the super user creation. With this I always get the error TypeError: create_superuser() missing 1 required positional argument: 'id'. I also tried to remove the id from the function. The process goes through without errors then, but I can't log in to the admin site, it says the username or password are not correct.
I have this in my admin.py:
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .forms import UserAdminCreationForm, UserAdminChangeForm
User = get_user_model()
# Remove Group Model from admin. We're not using it.
admin.site.unregister(Group)
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserAdminChangeForm
add_form = UserAdminCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ['name', 'admin']
list_filter = ['admin']
fieldsets = (
(None, {'fields': ('name', 'password')}),
('Personal info', {'fields': ()}),
('Permissions', {'fields': ('admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('name', 'password1', 'password2')}
),
)
search_fields = ['name']
ordering = ['name']
filter_horizontal = ()
admin.site.register(User, UserAdmin)
Do I need to modify the admin.py -file to make this work or is there some other obvious reason why it doesn't work?
In any case, it is really bad practice to give id value manually. the id should be auto-generated. But if you really want to get id as a function parameter, then you need to override the Django command to create a superuser.
You can read the details for the custom Django admin command here.
https://docs.djangoproject.com/en/3.2/howto/custom-management-commands/
Can I use same authentication system as provided by Django User models for models subclassing Abstract Base User models and, if not, then what else can we do? Also, how to configure ModelAdmin for providing admin access to these models?
The fact that you prefer using Django's built-in authentication system as is, is probably an indicator that substituting a custom User class based on the Abstract Base User model is unnecessary. You might want to consider extending the User class with a one-to-one relationship (how-to from the docs). This will allow you to keep all of Django's default behaviour, as well as add any additional fields, methods, etc. that you would want a User to have. We've found this to be adequate for most of our cases, and far less hassle than creating an entirely new Use class.
Anyways, that being said, you can create a custom authentication by inheriting from BaseUserManager and creating a new manager, then assigning your custom User's objects field to this new manager. Alternatively, you could assign your custom User's objects field to Django's manager. You should only create your own manager if you need some sort of custom authentication (for example, logging in with an email instead of with a username). Here is a link to the docs on how to do that.
To register the custom User model with Django's admin, you'll need to do a few things in your specific app's admin.py file. First, create custom forms for creating and editing Users, then subclass UserAdmin in your custom User Admin class, and reference the forms you previously created. Finally, register the new User Admin using admin.site.register(). Here's a full example of the admin.py file:
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from customauth.models import MyUser
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = MyUser
fields = ('email', 'date_of_birth')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
"""
A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = MyUser
fields = ['email', 'password', 'date_of_birth', 'is_active', 'is_admin']
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
class MyUserAdmin(UserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'date_of_birth', 'is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('date_of_birth',)}),
('Permissions', {'fields': ('is_admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'date_of_birth', 'password1', 'password2')}
),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
# Now register the new UserAdmin...
admin.site.register(MyUser, MyUserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
Once done with the admin.py file, specify the custom model as your default user model using the AUTH_USER_MODEL variable in your settings.py file.
AUTH_USER_MODEL = 'customauth.MyUser'
I want Django to send an email to user email-address with Login details once admin adds a new user to admin site.So I tried using Django signals for that but just becoz django user registration is a two step process signals get notified in first step only and called email function without email address(which comes in second step).
My signal code:
def email_new_user(sender, **kwargs):
if kwargs["created"]: # only for new users
new_user = kwargs["instance"]
send_mail('Subject here', 'Here is the message.', 'from#example.com',['to#example.com'], fail_silently=False)
post_save.connect(email_new_user, sender=User)
So what i tried to overcome this problem.I use this code in admin.py
class UserAdmin(admin.ModelAdmin):
list_display = ('username', 'email', 'first_name', 'last_name', 'date_joined', 'last_login')
search_fields = ['username', 'email']
filter_horizontal = ('user_permissions',)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
This makes all registration process a single step process and my signals start working and sending mail to user_id on new user addition.But the problem came after this were:
1. User password is not converted into hash and is visible while entering into form,that makes user not able to login into admin site.
2.Email field in form is not compulsory which I want to be compulsory.
Please help me :(
[EDIT]
I tried your code But I m still at same place where i was before posting this question.
the code i used in my admin.py is:
from django.contrib import admin
from mysite.naturefarms.models import *
from django.contrib.auth.models import User,Group
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django import forms
from django.contrib.admin.views.main import *
class MyUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'email',)
class UserAdmin(admin.ModelAdmin):
add_form = MyUserCreationForm
admin.site.unregister(User)
class MyUserAdmin(UserAdmin):
add_form = MyUserCreationForm
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2')}
),
)
admin.site.register(User, MyUserAdmin)
If you look in django.contrib.auth admin.py, you'll see that the UserAdmin class specifies the add_form as UserCreationForm.
UserCreationForm only includes the 'username' field from the User model.
Since you're providing your own UserAdmin, you can just override the add_form to a custom UserCreationForm that includes the fields you need to make your signal work properly.
Hope that helps you out.
[Edit]
Here's the UserCreationForm from contrib.auth forms.py:
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and password.
"""
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^[\w.#+-]+$',
help_text = _("Required. 30 characters or fewer. Letters, digits and #/./+/-/_ only."),
error_messages = {'invalid': _("This value may contain only letters, numbers and #/./+/-/_ characters.")})
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput,
help_text = _("Enter the same password as above, for verification."))
class Meta:
model = User
fields = ("username",)
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(_("A user with that username already exists."))
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
Notice the fields = ("username",) tuple which excludes all other fields on the User model. You need something like:
class MyUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'email',)
then you can use that as the add_form in your custom UserAdmin:
class UserAdmin(admin.ModelAdmin):
add_form = MyUserCreationForm
It's pretty late in my part of the world, but I'll see if I can get a working sample for you tomorrow.
[Edit]
Ok, here's the necessary changes you'll need to make to make this work. I've tested it using Django 1.3:
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django import forms
admin.site.unregister(User)
class MyUserAdmin(UserAdmin):
add_form = MyUserCreationForm
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2')}
),
)
admin.site.register(User, MyUserAdmin)
I didn't see that the UserAdmin had an add_fieldset property initially. That's why the email field wasn't displaying in the add form.
From this example try defining email in your custom UserCreationForm as required=True:
class MyUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ('username', 'email',)
I enabled the user auth module in Django, however when I use UserCreationForm it only asks for username and the two password/password confirmation fields. I also want email and fullname fields, all set as required fields.
I've done this:
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.contrib.auth.models import User
class RegisterForm(UserCreationForm):
email = forms.EmailField(label = "Email")
fullname = forms.CharField(label = "Full name")
class Meta:
model = User
fields = ("username", "fullname", "email", )
Now the form shows the new fields but it doesn't save them to the database.
How can I fix this?
There is no such field called fullname in the User model.
If you wish to store the name using the original model then you have to store it separately as a first name and last name.
Edit: If you want just one field in the form and still use the original User model use the following:
You can do something like this:
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.contrib.auth.models import User
class RegisterForm(UserCreationForm):
email = forms.EmailField(label = "Email")
fullname = forms.CharField(label = "First name")
class Meta:
model = User
fields = ("username", "fullname", "email", )
Now you have to do what manji has said and override the save method, however since the User model does not have a fullname field it should look like this:
def save(self, commit=True):
user = super(RegisterForm, self).save(commit=False)
first_name, last_name = self.cleaned_data["fullname"].split()
user.first_name = first_name
user.last_name = last_name
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
Note: You should add a clean method for the fullname field that will ensure that the fullname entered contains only two parts, the first name and last name, and that it has otherwise valid characters.
Reference Source Code for the User Model:
http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py#L201
You have to override UserCreationForm.save() method:
def save(self, commit=True):
user = super(RegisterForm, self).save(commit=False)
user.fullname = self.cleaned_data["fullname"]
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/forms.py#L10
In django 1.10 this is what I wrote in admin.py to add first_name, email and last_name to the default django user creation form
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib import admin
from django.contrib.auth.models import Group, User
# first unregister the existing useradmin...
admin.site.unregister(User)
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
fieldsets = (
(None, {'fields': ('username', 'password')}),
('Personal info', {'fields': ('first_name', 'last_name', 'email',)}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
('Important dates', {'fields': ('last_login', 'date_joined')}),)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'first_name', 'last_name', 'password1', 'password2')}),)
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
search_fields = ('username', 'first_name', 'last_name', 'email')
ordering = ('username',)
filter_horizontal = ('groups', 'user_permissions',)
# Now register the new UserAdmin...
admin.site.register(User, UserAdmin)