I have a custom user model, which an admin model inherits:
class User(AbstractBaseUser, PermissionsMixin):
...
class Staff(User):
is_staff = models.BooleanField(
_("staff status"),
default=True,
help_text=_("Designates whether the user is staff"),
)
...
class Admin(Staff):
is_admin = models.BooleanField(
_("staff status"),
default=True,
help_text=_("Designates whether the user can log into this admin site."),
)
...
As well as a custom backend for authorization
class AdminBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(Admin.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = Admin._default_manager.get_by_natural_key(username)
except Admin.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
Admin().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
...
However, I am unable to log in due to has_permission in admin/sites.py
Is there a way to avoid registering a custom site, as that will increase complexity?
Related
Extended from: Drf how to: simple-jwt authenticating without the USERNAME_FIELD
I was trying to figure out how to authenticate a user with a field that is not set as the USERNAME_FIELD and faced some issues, it lets me input in the correct data fields, but it never authenticates
I'm using this snippet from the previous questions answer:
class MyTokenStudentSerializer(TokenObtainPairSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['student_id'] = serializers.CharField(required=False)
# self.fields['password'] = serializers.CharField(write_only=True, required=True)
self.fields['password'] = PasswordField(trim_whitespace=False)
username_field = 'student_id'
auth_fields = ['student_id']
#login view extended from TokenObtainPairView
class LoginStudentView(TokenObtainPairView):
permission_classes = (AllowAny,)
serializer_class = MyTokenStudentSerializer
produces
{
"detail": "No active account found with the given credentials"
}
any modifications would be greatly appreciated.
If you are using default ModelBackend you should specify USERNAME_FIELD
class User(AbstractUser):
USERNAME_FIELD = 'student_id'
student_id = models.TextField(default="", unique=True) # Should be unique
Output
~ $ curl -X POST "http://127.0.0.1:8000/api/auth/login-student/" -d "password=admin&student_id=stdnt"
{"refresh":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY0MzcxMTgzMCwiaWF0IjoxNjQxMTE5ODMwLCJqdGkiOiJkY2MyNTEwZGRiNWE0ZTJmODllMDI2OWRkYWI5ZGVjNSIsInVzZXJfaWQiOjF9.c0QTdBhiPUf4yvPP0l3a-XQ0iD6kycECAdb6MAROY8g","access":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjQxMTIzNDMwLCJpYXQiOjE2NDExMTk4MzAsImp0aSI6ImM0NjA0ZTlhMDBhNjQ5YjdhMTkxOGQ3OTJmOTMyYTJiIiwidXNlcl9pZCI6MX0.XoZXTJICE_PyZFXIIvsm3bci-e-O67AsYvIvY1ijNAo"}
Also, you can write your own auth backend and include it in settings.py.
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'project.apps.auth.backends.MyCustomModelBackend',
]
Example of backend
class MyCustomModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
student_id = kwargs.get("student_id")
if student_id is None or password is None:
return
try:
user = User.objects.get(student_id=student_id)
except User.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
User().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
With this approach you can preserve login via username field. Also you shouldn't specify USERNAME_FIELD = 'student_id' in User model
I'm working on a Django system where there are four levels of users:
1. Basic (access only)
2. Admin (can update/change/delete)
3. Gatekeeper (can only create Admin users, cannot update/change/delete)
4. Developer (true superuser)
I think I have the permissions somewhat figured out:
from django.conf import settings
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
from django.core.validators import RegexValidator
from django.db import models
from django.db.models.signals import post_save
class CustomUserManager(BaseUserManager):
"""Customer User."""
def create_user(self, email, password=None):
"""Creates and saves a user."""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_admin(self, email):
"""Creates and saves an admin user with a temporary password."""
user = self.create_user(
email,
password=BaseUserManager.make_random_password(self, 12)
)
user.is_admin = True
user.save(using=self.db)
return user
def create_gatekeeper(self, email, password):
"""Creates and saves a gatekeeper."""
user = self.create_user(
email,
password=password,
)
user.is_admin = True
user.is_gatekeeper = True
user.save(using=self.db)
return user
def create_superuser(self, email, password):
"""Creates and saves a superuser."""
user = self.create_user(
email,
password=password,
)
user.is_admin = True
user.is_gatekeeper = True
user.is_developer = True
user.save(using=self._db)
return user
class CustomUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_gatekeeper = models.BooleanField(default=False)
is_developer = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
# def __str__(self):
# return self.email
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
# Does the user have a specific permission?
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
# "Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
class Meta:
db_table = 'customuser'
verbose_name = 'CustomUser'
However, I'm not making the connection with how to create gatekeepers and admins. I know I can do it via the command line, but I want
a) a form where developers can create gatekeepers
b) a form where gatekeepers can create admins
Since our administrative site will need to be heavily customized, we will not be using the Django Admin and will likely be building our own functionality. How would I go about calling something like create_gatekeeper inside of a Django form? Would subclassing the Django Admin pages and customizing those be a better way to go?
You probabily should work with Groups and add permissions to those groups;;; but in 3 of yours Actors you can use django builtin attributes from User models...
User: You regular user just create as User class with anything else as is_something...
Admin You can use attribute is_staff that attribute comes from User Models, and allow your user to access django admin...
Developer You can create him as Super User, so all permissions is self added to your models
The thing is about gatekeeper, so if you start to create flags as is_admin, is_gatekeeper and etc... you will start to handle multiple attributes, and this is bad ideia, so when you are working with groups you can create User Group, Developer Group (since they are super admin you dont really need to do that), Admin Group (add each permission you want to give to this groups [Eg. Blog Models you can give to him blog add_blog, change_blog and delete_blog, but you can add your custom too]) same as other groups...
Eg.
# List of my Permissions that i want to add to my groups
PERMISSIONS = {
STUDENT : ['add_student', 'change_student',
'delete_student', 'editpreview_student'],
PROJECT: ['add_project', 'change_project', 'delete_project', 'editpreview_project'],
}
# Creating 2 Groups
grupo_admin, created = Group.objects.get_or_create(name='admin')
grupo_teachers, created = Group.objects.get_or_create(name='teacher')
for func_perm in PERMISSIONS [STUDENT]:
perm = Permission.objects.get(codename=func_perm)
grupo_admin.permissions.add(perm) # Adding Student Permission to my Admin Group
grupo_teachers.permissions.add(perm) # Adding Student Permission to my Teachers Group
for func_perm in PERMISSOES[PROJECT]:
perm = Permission.objects.get(codename=func_perm)
grupo_admin.permissions.add(perm) # Adding Project Permission only to my Admin Group
In your views you can check the permission like that
#user_passes_test(lambda u: u.has_perm('myapp.permission_code'))
def some_view(request):
# ...
And your HTML you can check like that
{% if perms.student.change_student %}
<li>
<a href="/admin/student/student/">
<i class="fa fa-graduation-cap" aria-hidden="true"></i>
<span>Students</span>
</a>
</li>
{% endif %}
How to create login for different user i.e in my case there is two user (client and advertizer) and I want to redirect to specify page according to user type, here is my models.py and views.py, and also how to check authentication for different user type, and there is only one login page for all users:
# models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Client(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='client_profile')
#first_name = models.CharField(max_length=30)
#last_name = models.CharField(max_length=40)
#username = models.CharField(max_length=30)
buisness_name= models.CharField(max_length=30, blank=True)
USER_TYPE_CHOICES = (
('c', 'client'),
('a', 'advertizer'),
)
type_user = models.CharField(max_length=20, default='c', choices=USER_TYPE_CHOICES)
class Advertizer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='advertizer_Profile')
USER_TYPE_CHOICES = (
('c', 'client'),
('a', 'advertizer'),
)
type_user = models.CharField(max_length=20, default='a', choices=USER_TYPE_CHOICES)
# views.py
def user_login(request):
if request.method == 'POST':
# First get the username and password supplied
username = request.POST.get('username')
password = request.POST.get('password')
# Django's built-in authentication function:
user = authenticate(username=username, password=password)
# If we have a user
if user:
#Check it the account is active
if user.is_active:
# Log the user in.
login(request)
if request.Client.user.type_user == 'c':
# Send the user back to some page.
# In this case their homepage.
return render(request, 'client.html')
else:
# If account is not active:
return HttpResponse("Your account is not active.")
else:
print("Someone tried to login and failed.")
print("They used username: {} and password: {}".format(username, password))
return HttpResponse("Invalid login details supplied.")
else:
#Nothing has been provided for username or password.
return render(request, 'login.html', {})
You have to models - Client and Advertizer. When you got client from user that have only Advertizer, it will raise attribute error. (Cause your user object just have one of client or advertizer object)
You have few choices.
First, just move type_user to your User model. Then you just get your user type from user. like below
if request.user.type_user == 'c':
...
I recommend this method cause user_type is quite important information and you may divide it very often. So just add it to user and make your own UserManager. Then you can just call like User.objects.client.all().
Second, easy way for now is just add try/except. You got error from the user don't related to Client object. So just add try and if raise attribute error, it's for Advertizer type user. Like below
try:
type_user = request.user.client.type_user
except AttributeError:
type_user = request.user.advertizer.type_user
if type_user == 'c':
# Send the user back to some page.
It's easy way, but you have to add try/except for every time you want to divide your user model. So it's easy to add it to your user model itself.
Ive been running into a number of problem in relation to using django's custom model. This one in particular is not raising any errors. For some reason after authenticating via steam and returning to the landing page the database tables for both steamuser_user (custom user) and social_auth_usersocialauth are empty. Nothing is being saved, no errors are being displayed etc.
My custom model which is quite similar to the one on django docs official page is as follows:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
# Create your models here.
class UserManager(BaseUserManager):
def create_user(self, steamid, username, password=None):
if not steamid:
msg = 'User has no Steam ID set'
raise ValueError(msg)
if not username:
msg = 'User has no name set'
raise ValueError(msg)
user = self.model(steamid=steamid,
username=username)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, steamid, username, password):
super_user = self.create_user(steamid=steamid,
username=username,
password=password)
super_user.is_staff = True
super_user.is_admin = True
super_user.is_mod = True
super_user.save(using=self._db)
return super_user
class User(AbstractBaseUser):
steamid = models.CharField(max_length=20, unique=True)
username = models.CharField(max_length=80)
email = models.EmailField(null=True,blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_mod = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
reputation = models.IntegerField(max_length=6, default=0)
USERNAME_FIELD = 'steamid'
objects = UserManager()
def __unicode__(self):
return self.username
def get_full_name(self):
return self.steamid
def get_short_name(self):
return self.username
The settings I've used are as follows:
SOCIAL_AUTH_USER_MODEL = 'steamuser.User'
AUTH_USER_MODEL = 'steamuser.User'
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'social.apps.django_app.context_processors.backends',
'social.apps.django_app.context_processors.login_redirect',
)
AUTHENTICATION_BACKENDS = (
'social.backends.steam.SteamOpenId',
'django.contrib.auth.backends.ModelBackend',
)
#Steam OpenAuth
SOCIAL_AUTH_STEAM_API_KEY = 'B1D7C629D093D4B72577F2F11DE4EBE2'
LOGIN_URL = '/'
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/'
SOCIAL_AUTH_ENABLED_BACKENDS = (
'steam',
)
Any help would be appreciated!
EDIT
Backends steam.py
def get_user_details(self, response):
player = self.get_json(USER_INFO, params={
'key': self.setting('API_KEY'),
'steamids': self._user_id(response)
})
if len(player['response']['players']) > 0:
player = player['response']['players'][0]
details = {'steamid': player.get('steamid'),
'username': player.get('personaname'),
}
else:
details = {}
return details
EDIT 2
Well despite my logical reasoning, I just gave up and created a custom pipeline to create the new steam user as follows:
from django.contrib.auth import get_user_model
def create_steamuser(details, user=None, *args, **kwargs):
if user:
return {'is_new': False}
if not details:
return
try:
steam_user = get_user_model().objects.get(steamid=details['steamid'])
except steam_user.DoesNotExist:
get_user_model().objects.create_user(details['steamid'], details['username'])
return {
'is_new': True,
}
Now I still have the problem where social_user is not being created. I've set the social user model to use my new custom model but there must be something that I am missing.
python-social-auth won't be able to pass the steamid and date_joined parameters to your custom create_user() method in the manager. To make that possible you have three options:
Set =None to those parameters and set some default vaules for them
Override the default create_user pipeline and pass the extra parameters.
Add a custom pipeline function before create_user and fill details with steamid and date_joined, then define SOCIAL_AUTH_STEAM_USER_FIELDS = ('username', 'email', 'steamid', 'date_joined').
I am using Django1.4 with PostgreSQL. I am developing an application in which I have two models i.e. Students, Company.
class students(models.Model):
first_name = models.CharField(**option)
last_name = models.CharField(**option)
username = models.EmailField(max_length=100, unique=True)
password = models.CharField(_('password'), max_length=128)
# Some other attributes for Student models
class company(models.Model):
compnay_name = models.CharField(**option)
username = models.EmailField(max_length=100, unique=True)
password = models.CharField(_('password'), max_length=128)
#Some other attributes for company models
My Requirement:
Student and Company can create a new profile (provide a sign-up form)
Which creating a new profile for Student/Company, username i.e. email id should be unique. i.e. Email id should not exist in Student & Company models.(task completed )
Created 2 sign-In form for Student & Company login.
Issue:
As I am not using or extending User model, I am cannot use django in-built login & authenticate method.
How can I write a custom authentication method which should check user credentials in Student/Company username & password. (Have 2 different Sign-in form for Student & Company)
Please help me.
Thanks for reading my query.
backend.py
class LoginBackend:
def authenticate(self, username=None, password=None, model=None):
if model == "Student":
lookup_model = Student
elif model == "Employer":
lookup_model = Employer
try:
user = lookup_model.objects.get(email=username)
except Exception, e:
return None
return user
views.py
def check_auth(request):
user_object = Student.objects.get(email__iexact = unicode(email))
if check_password(password, user_object.password):
print authenticate(username = email, password = password, model = "Student")
login(request, user_object)
settings.py
AUTHENTICATION_BACKENDS = ("proj.app.backends.LoginBackend",)
Error
AttributeError at /xxx/login/
'Student' object has no attribute 'backend'
Write a custom authentication backend. Read this:
Writing an authentication backend
Handling authorization in custom backends
settings.AUTHENTICATION_BACKENDS
[update]
By writing and registering a custom authentication backend, you just have to use the standard Django authentication patterns. Looking at your sample code, I'm under the impression that you have understood it differently.
Since email is your unique key, I suggest using email for the login key, first check the login/password against Student, and if it fails, check against Company.
from django.contrib.auth.models import User
class JayapalsBackend(object):
def authenticate(self, username=None, password=None):
try:
o = Student.objects.get(email=username, password=password)
except Student.DoesNotExist:
try:
o = Company.objects.get(email=username, password=password)
except Company.DoesNotExist:
return None
return User.objects.get(email=o.email)
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Then just use standard Django decorators:
#login_required
def some_private_view(request):
...