Django - PermissionRequiredMixin with custom user model as well as AUTHENTICATION_BACKENDS - django

I am using Django 1.9. When I tried to add PermissionRequiredMixin to my class-based-view, it seems not to work as expected. I created a new user in a auth_group. This auth_group doesn't have any permission to any apps or models. This new user is not a superuser or admin user. But the app doesn't prevent this user from accessing to a particular view that needs permission_required.
Firstly, here is what I tried to make sure the user doesn't have permission:
user.get_all_permissions() # return set() - empty permission, which is correct.
user.is_superuser # return false, which is correct.
user.has_perm('myapp.add_something or even any words that make no sense') # always return true, which is very weird.
The app has custom user model and also uses django-allauth as the AUTHENTICATION_BACKENDS. I am not sure if PermissionRequiredMixin will check user.has_perm() and it always return true so that's why checking permission doesn't work as expected?
# views.py
class My_View(PermissionRequiredMixin, View):
permission_required = 'polls.can_vote'
def get(self, request, *args, **kwargs):
# do something...
return render(request, "template.html", {})
# models.py - Custom User Model
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
group = models.ManyToManyField(Group, through='UserGroupRelationship')
....
# models.py - many-to-many relationship between user and group
class UserGroupRelationship(models.Model):
user = models.ForeignKey("CustomUser")
user_group = models.ForeignKey(Group)
I also tried the old way to check permission in urls.py. It doesn't prevent user accessing either so I do not think that's the problem of using PermissionRequiredMixin.
urlpatterns = patterns('',
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
)

After spending a few days on this problem, I eventually find out the cause.
When I looked into the source code about PermissionRequiredMixin, I found that PermissionRequiredMixin indeed checks user.has_perm(). When I tried to find the source code of has_perm(), I found that my codes (which is copied from the custom user model example from Django's document) contains the following overridden method...
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
That's reason why user.has_perm('anything') always return true, which also affects the functionality of PermissionRequiredMixin. Therefore, if you are new to Django and try to copy some example codes from document, you need to be very careful about each line...

Related

Django auth groups not finding groups and does not give permission to users, regardless of the group in which they are

I'm making a simple real estate app where the users must be separated into two different groups. A regular user and a broker. I'm using the Django admin backend for creating user groups. I'm also using a post_save signal to assign all the new users to the regular users' group. At first glance, it works well and in Django admin, it shows me that the user is in the corresponding group. But when I try to print self.request.user.groups the value is None. Also when I try to check if users have permission to do something, regardless of the permissions I gave the view I check always gives me 403 Forbidden regardless of whether the user has permission or not. I use class-based views and PermissionRequiredMixin respectively.
Here is my user model:
class DjangoEstatesUser(auth_models.AbstractBaseUser, auth_models.PermissionsMixin):
USERNAME_MAX_LENGTH = 30
username = models.CharField(
max_length=USERNAME_MAX_LENGTH,
unique=True,
)
date_joined = models.DateTimeField(
auto_now_add=True,
)
is_staff = models.BooleanField(
default=False,
)
USERNAME_FIELD = 'username'
objects = DjangoEstatesUserManager()
Also the signal for assigning the new users to a group:
#receiver(post_save, sender=DjangoEstatesUser)
def create_user_profile(sender, instance, created, **kwargs):
if created:
instance.groups.add(Group.objects.get(name='Users'))
and this is one view that I'm trying to restrict for different users:
class AddEstateView(auth_mixins.LoginRequiredMixin, auth_mixins.PermissionRequiredMixin, views.CreateView):
permission_required = 'auth_permission.add_estate'
template_name = 'main/estates/add_estate.html'
form_class = AddEstateForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def get_success_url(self):
return reverse_lazy('profile details', kwargs={'pk': self.object.user_id})
And these are the groups with the permissions.
brokers_group
and
users_group.
And these are my
db_tables
and auth_permission_table.
I can't figure out where I'm wrong or what I'm not doing right, any help will be appreciated!
Thank you in advance!
self.request.user.groups prints always None: that is a RelatedManager [Django-doc], not a QuerySet. Managers will print the model name with None, so for a Model named ModelName in app_name, it prints:
app_name.ModelName.None
You print the items with self.request.user.groups.all():
print(self.request.user.groups.all())

Steps to add model level permission in Django

I have my legacy database.I created models using inspectdb.I am able to see my tables in admin page . I created 4 users and i want to implement role-based permission for each model. I just show you with example what i did in model to add permissions like edit, view, delete.
Example:-
class BusinessIntegrationSourceCategory(models.Model):
date_added = models.DateTimeField(blank=True, null=True)
date_modified = models.DateTimeField(blank=True, null=True)
source_category = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'business_integration_source_category'
permissions = (
("view_category", "view category"),
("add_category", "Add category"),
("delete_category", "Delete category"),
)
Now where i can add the next steps to add role based permissions.
This from The Django docs.
Handling object permissions
Django’s permission framework has a foundation for object permissions,
though there is no implementation for it in the core. That means that
checking for object permissions will always return False or an empty
list (depending on the check performed). An authentication backend
will receive the keyword parameters obj and user_obj for each object
related authorization method and can return the object level
permission as appropriate.
So as said: An authentication backend will receive the keyword parameters obj and user_obj.
And these tow variables are the seeds of object level permissions, but the default backend django.contrib.auth.backends.ModelBackend does not take advantage of that. So you should create a custom backend.
Note:
If you use custom User model, your user model should subclass PermissionsMixin, because The has_perm method of PermissionsMixin passes the work off to the registered authentication backends.
Try:
backends.py file:
from django.contrib.auth import get_user_model
class MyAuthenticationBackend:
def authenticate(self, *args, **kwargs):
pass
def get_user(self, user_id):
try:
return get_user_model().objects.get(pk=user_id)
except get_user_model().DoesNotExist:
return None
def has_perm(self, user_obj, perm, obj=None):
if perm == "view_category":
return True # everybody can view
# otherwise only the owner or the superuser can delete
return user_obj.is_active and obj.user.pk==user_obj.pk
def has_perms(self, user_obj, perm_list, obj=None):
return all(self.has_perm(user_obj, perm, obj) for perm in perm_list)
In settings.py file add:
AUTHENTICATION_BACKENDS = [
'your_app.backends.MyAuthenticationBackend',
# Your other auth backends, if you were using the default,
# then comment out the next line, otherwise specify yours
# 'django.contrib.auth.backends.ModelBackend'
]
Note: every model already has default permissions (add, change and delete)
I hope this will push you forward.

Django Authenticate returns None

I have the following code snippet:
user = User(username='h#h.com',email='h#h.com')
user.set_password('pass')
user.save()
u = authenticate(username='h#h.com', password='pass') #this always returns None!!!
The problem is, u is always None. I've followed code samples on other stack overflow posts and have narrowed it down to the above lines.
Any ideas as to what might be happening?
Put something like this in your settings
#Authentication backends
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
or if you are using userena for your accounts
#Authentication backends
AUTHENTICATION_BACKENDS = (
'userena.backends.UserenaAuthenticationBackend',
'guardian.backends.ObjectPermissionBackend',
'django.contrib.auth.backends.ModelBackend',
)
Interestingly enough, check_password returns True in the following:
eml = "4#a.com"
pw = "pass"
uname = 'w2'
user = User.objects.create_user(uname,eml,pw)
user.save()
log.debug("Password check passes?")
log.debug(user.check_password(pw)) # Logs True!!!
user = authenticate(username=uname, password=pw)
Why don't you create a user like this:
user = User.objects.create_user( username="whatever", email="whatever#some.com", password="password")
user = authenticate( username="whatever",password="password")
In settings.py, add
AUTH_USER_MODEL = your custom user class
e.g if django app name is office and custom user class is Account then
AUTH_USER_MODEL = 'office.Account'
set_password is a misleading method, it doesn't save the password on the user table. You need to call user.save() in order for it to work on your flow
You have to check whether user is active? If not, you only set active for user in admin panel, or set when creating user by adding the following line to user model:
is_active = models.BooleanField(default=True)
Also check that you have the right username/password combo. sometimes the one that is created from the createsuperuser command is different than a username you would typically use.
As most of them suggested if we create the user's using User.objects.create_user(**validated_data) this will hash the raw password and store the hashed password. In-case if you you are using User model serializers to validate and create users, it is required to override the serializer method like this
class UserSerializers(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
# this is the method responsible for insertion of data with hashed password
def create(self, validated_data):
return User.objects.create_user(**validated_data)
I puzzled with this problem for four days, and the above answers didn't help.
The problem was that I, as you, was using the user.save() and I could not see what the problem was, but then I looked at it with debug eyes, and it turns out that if you use user.save() it messes with the hashing of the password somehow, don't ask me how I don't know. So I worked around it by using the user.create() method that Django provides, worked like a charm:
#api_view(['POST'])
def create_user(request):
new_user = UserSerializer(data=request.data)
if new_user.is_valid():
user_saved = new_user.create(request.data)
return Response('User {} created'.format(user_saved.username),
status=status.HTTP_200_OK)
else:
return Response('User not created', status=status.HTTP_200_OK)
I used something like this, but you can do as you wish just use the user.create().

'User' Object has no attribude is_authenticated

I've created a User model for my django app
class User(Model):
"""
The Authentication model. This contains the user type. Both Customer and
Business models refer back to this model.
"""
email = EmailField(unique=True)
name = CharField(max_length=50)
passwd = CharField(max_length=76)
user_type = CharField(max_length=10, choices=USER_TYPES)
created_on = DateTimeField(auto_now_add=True)
last_login = DateTimeField(auto_now=True)
def __unicode__(self):
return self.email
def save(self, *args, **kw):
# If this is a new account then encrypt the password.
# Lets not re-encrypt it everytime we save.
if not self.created_on:
self.passwd = sha256_crypt.encrypt(self.passwd)
super(User, self).save(*args, **kw)
I've also created an authentication middleware to use this model.
from accounts.models import User
from passlib.hash import sha256_crypt
class WaitformeAuthBackend(object):
"""
Authentication backend fo waitforme
"""
def authenticate(self, email=None, password=None):
print 'authenticating : ', email
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = None
if user and sha256_crypt.verify(password, user.passwd):
return user
else:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
I have ammended the settings.py file correctly and if I add some print statements to this backend I can see the user details print out. I don't recall reading that I need to implement is_authenticated in the django docs. Am I missing something silly?
I'm not quite sure why you have created a new User model instead of using Django's built-in one and adding a linked UserProfile, which is the recommended thing to do (until 1.5 is released, when pluggable user models will be available). However, yes you need to define an is_authenticated method, which always returns True: this is exactly what the built-in model does. The reason is that if you have an actual User, it will always be authenticated: otherwise, you will have an AnonymousUser object, whose is_authenticated method always returns False.
you dont have to reinvent the wheel. Just use Djangos build in authentication system and save yourself a lot of trouble. You can also extend it to your needs or use different authentication backends. Have a read here. HTH.

Profile page getting acess to user object in Django

I have a requirement where I have to register users first via email. So, I went with django-registraton and I managed to integrate tat module into my django project.
After a successful login, the page redirects to 'registration/profile.html'.
I need to get access to the user object which was used in the authentication.
I need this object to make changes to a model which holds custom profile information about my users. I have already defined this in my models.py
Here is the URL I am using to re-direct to my template..
url(r'^profile/$',direct_to_template,{'template':'registration/profile.html'}),
So my question is this... after login, the user has to be taken to a profile page that needs to be filled up.
Any thoughts on how I can achieve this?
I have set up something similar earlier. In my case I defined new users via the admin interface but the basic problem was the same. I needed to show certain page (ie. user settings) on first log in.
I ended up adding a flag (first_log_in, BooleanField) in the UserProfile model. I set up a check for it at the view function of my frontpage that handles the routing. Here's the crude idea.
views.py:
def get_user_profile(request):
# this creates user profile and attaches it to an user
# if one is not found already
try:
user_profile = request.user.get_profile()
except:
user_profile = UserProfile(user=request.user)
user_profile.save()
return user_profile
# route from your urls.py to this view function! rename if needed
def frontpage(request):
# just some auth stuff. it's probably nicer to handle this elsewhere
# (use decorator or some other solution :) )
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
user_profile = get_user_profile(request)
if user_profile.first_log_in:
user_profile.first_log_in = False
user_profile.save()
return HttpResponseRedirect('/profile/')
return HttpResponseRedirect('/frontpage'')
models.py:
from django.db import models
class UserProfile(models.Model):
first_log_in = models.BooleanField(default=True, editable=False)
... # add the rest of your user settings here
It is important that you set AUTH_PROFILE_MODULE at your setting.py to point to the model. Ie.
AUTH_PROFILE_MODULE = 'your_app.UserProfile'
should work.
Take a look at this article for further reference about UserProfile. I hope that helps. :)