How to change is_staff option in Django admin at template level - django

I have to change the "is_staff" option from views.py file to disabling the Django-admin page but i am not able to figure out this following issue.
Whenever i tried to write "user.is_staff" then it sound that there is not any option to choose it (is_staff) whereas is_active is present. is this problem of importing?
Following are the content which i am importing:
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404
from django.views.generic.list_detail import object_detail
from django.views.generic.simple import direct_to_template
from django.utils.translation import ugettext as _
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
Following code i wrote in views.py:
def user_change_status(request, id):
user = User.objects.get(pk=id)
if user.is_staff:
user.is_staff = False
else:
user.is_active = True
return HttpResponse('')
Whole scenario is that i have a template which is showing the list of all user with his/her is_staff option (True/ False). What i want when superuser will select any of user is_staff option it become change and the page will redirect on the same page.
After editing:
Two method are defined in views.py:
def user_change_status(request, id):
user = User.objects.get(pk=id)
if user.is_active:
user.is_staff = False
else:
user.is_staff = True
user.save()
value2 = user.is_staff
return HttpResponse(value2)
and another one is `
def user_block(request, id):
user = User.objects.get(pk=id)
if user.is_active:
user.is_active = False
else:
user.is_active = True
user.save()
value1 = user.is_active
return HttpResponse('value1')
I want to change the is_staff value and is_active value. Method user_change_status is not working whereas user_block does.

Python is a dynamic language. In particular, it's not Java or C++. As a rule, IDEs do very badly in autocompleting in dynamic languages.
It's a complete mistake to take any notice of what your IDE does or doesn't offer as autocomplete options. Sometimes it'll get it right, other times it won't. Sometimes it'll offer options that aren't members of the object at all.
Use the documentation, not your IDE.

user_change_status removes the is_staff for active users, but enables the is_staff for inactive users, is this what you wanted to do ? Is it not about toggling the is_staff value, actually ?
I ask since the user_block toggles the is_active value.
If so, you should replace
if user.is_active:
user.is_staff = False
with
if user.is_staff:
user.is_staff = False

Related

My user roles returns either list index out of range or takes me back to log in even though the right role is logged in

I have created user roles within my Django application and have designated users their roles within the admin interface but when I log in an try to access the restricted access page using the correct role I am either returned to the log in page or met with a list index out of range error and I am very confused to as why this is.
Models.py
from pickle import TRUE
from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.utils import timezone
ROLES = (
('0','Researcher'),
('1','Clinician'),
('2','Superuser'),
)
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User, on_delete=models.CASCADE)
# The additional attributes we wish to include.
picture = models.ImageField(upload_to='profile_images', default='default/default.jpg', blank=True)
role = models.CharField(max_length=1000, choices=ROLES, default='0')
is_active = models.IntegerField(default = 1,
blank = True,
null = True,
help_text ='1->Active, 0->Inactive',
choices =(
(1, 'Active'), (0, 'Inactive')
))
created_on = models.DateTimeField(default = timezone.now)
updated_on = models.DateTimeField(default = timezone.now,
null = True,
blank = True
)
Views.py
from django.shortcuts import render, redirect
from django.urls import reverse
from django.http import HttpResponse
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from parasites_app.models import Contact, UserProfile,Category,Photo
from parasites_app.forms import ContactForm
from django.core.mail import send_mail
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test
#for the permissions decorators
def find_role(u):
userprofile = UserProfile.objects.filter(user = u)[0]
return userprofile.role
#create decorators to dictate permissions
##clinician_required
def clinician_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url='login'):
actual_decorator = user_passes_test(
lambda u: u.is_active and find_role(u) == 'Clinician',
login_url=login_url,
redirect_field_name=redirect_field_name
)
if function:
return actual_decorator(function)
return actual_decorator
#clinician_required
def Clinician(request):
category = request.GET.get('category')
if category == None:
photos = Photo.objects.all()
else:
photos = Photo.objects.filter(
category__name=category)
categories = Category.objects.all()
context = {'categories': categories, 'photos': photos}
return render(request,'parasites_app/Clinician.html',context)
I think the issue might be with find_role(u) == 'Clinician'
The role returned from find_role is the numeric key stored for that tuple of choices. Perhaps find_role can be rewritten as below:
def find_role(u):
userprofile = UserProfile.objects.filter(user = u)[0]
return userprofile.get_role_display()
I might also suggest that you move the logic of what determines the user to be a clinitian onto the user model. e.g. create a is_clinician method that returns true or false and use that in the check for permission perhaps. Just a suggestion.
I also noticed that the max_length for the role field is 1000, this might take up more space in the DB than you need and you'll feel the pain later when you have thousands of entries and need to shrink the DB, since the role is just one char you can set the max_length to 1 and you should be fine, or even 10 in case you want to expand it at some point, but 1000 off the bat might just cause the DB to be unnecessarily large.

How to prevent the logged in user to access the profile of other user by simply changing the id in url , in django?

I am currently building a small project using Django, I have noticed a problem that a logged in user was getting access to the other users page by simply changing the id in the url i.e
This is the url of currently logged in user
http://localhost:8000/home/myBooks/7/
by changing that id from 7 to 6
i.e
http://localhost:8000/home/myBooks/6/
He was getting access to that page,I have used #login_required for functional based views and LoginRequiredMixin for class based views ,but they are not helping, what else I need to do to prevent this problem?
My app/views.py:
from django.shortcuts import render,redirect
from django.http import HttpResponse
from django.views.generic.edit import FormView
from . forms import BookForm
from django.contrib.auth.models import User
from . models import UserBooks
from django.contrib.auth.models import User
from django.views import generic
from django.contrib.auth.decorators import login_required
from .models import UserBooks
from django.shortcuts import get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin
#login_required
def HomeView(request):
return render(request,'home/homepage.html')
class BookDetailsView (LoginRequiredMixin,generic.DetailView):
model=UserBooks
template_name='home/bookdetails.html'
class BooksView (LoginRequiredMixin,generic.DetailView):
model=User
template_name='home/mybooks.html'
#login_required
def addBooks(request):
if (request.method=='POST'):
form=BookForm(data=request.POST)
if(form.is_valid()):
u=UserBooks()
u.book_name=form.cleaned_data['book_name']
u.book_author=form.cleaned_data['book_author']
u.book_ISBN=form.cleaned_data['book_ISBN']
u.book_status=True
u.book_genre=form.cleaned_data['book_genre']
u.username=request.user.username
u.user_id = User.objects.get(username=request.user.username)
u.save()
return redirect('/')
else:
form = BookForm()
return render (request,'home/addbooks.html',{'form':form})
my apps/models.py:
from django.db import models
from django.contrib.auth.models import User
class UserBooks(models.Model):
user_id = models.ForeignKey(User,on_delete=models.CASCADE,null=True)
username = models.CharField(max_length=200)
book_name = models.CharField(max_length=200)
book_author = models.CharField(max_length=200)
book_ISBN=models.CharField(max_length=200)
book_genre = models.CharField(max_length=200)
book_status=models.BooleanField(default=False)
class Meta:
unique_together = (("username", "book_ISBN"),)
def __str__(self):
return self.book_name
my apps/urls.py:
from django.urls import path
from . import views
app_name='home'
urlpatterns=[
path('',views.HomeView,name='home'),
path('addBooks/',views.addBooks,name='addBooks'),
path('myBooks/<int:pk>/',views.BooksView.as_view(),name='myBooks'),
path('<int:pk>/', views.BookDetailsView.as_view(), name='myBooks'),
]
If your view should always show the detail for the current user, don't put the ID in the URL at all; get the logged-in user directly within the view.
class BooksView(LoginRequiredMixin, generic.DetailView):
model = User
template_name ='home/mybooks.html'
def get_object(self):
return self.request.user
...
path('myBooks/',views.BooksView.as_view(),name='myBooks'),
class BooksView(LoginRequiredMixin, DetailView):
...
def get(self, request, *args, **kwargs):
current_user = User.objects.get(id=self.request.user.pk)
if current_user.pk == kwargs['pk']:
return HttpResponseRedirect('/')
else:
return HttpResponseRedirect('profile-url')
Here I assume that if you are logged in user and you try to check another user profile by giving id in url. So I add a get method which will check is requested URL id is for the current user (books/7/ is 7 is current user id) if not then redirect to an URL, for example, otherwise redirects to another url. You can get some idea. This may not help you exactly.
If you have just started to develop the app, then it is okay to use pks inside of urls. However, when it comes to a real working app it can lead to some security problems.
As you wrote, one can simply change the url and get some private data.
Other problems can be:
The number of users in the database can be easily counted by iteration through your urls.
The user can be easily detected by his id. Knowing it one can easily get some private data.
If you will decide to change ids in your db, then all the external links will be broken...and etc.
Considering that I suggest an approach in which you use ids internally. For external usage (urls, links) you can use uuids.
To do that you just need additional field into your model:
import uuid
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
And here is the example url:
url(r'^myBooks/(?P<user_uuid>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)/$',
After you switch to uuids it will be almost impossible to "hack" the url.

Django Create a user using only Email and set as inactive

In my app I have 3 kind of users with different permissions. HR, Employees and candidate.
I would like to be able to create a User employee providing only its email ans set it as inactive And when the employee sign in into the app he setup a his first name, last name and a password and become Active on the app
I am starting in django and have really no idea how to do it;
I started the authentication process using the documentation and I get to :
views.py :
from django.shortcuts import render
from .forms import HRForm, CandidateForm, EmployeeForm
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth import authenticate,login,logout
from django.contrib.auth.backends import ModelBackend
from .models import MyUser
def registerEmployee(request):
registered = False
if request.method == "POST":
Employee_form = EmployeeForm(data=request.POST)
if Employee_form.is_valid():
user = Employee_form.save()
user.set_password(user.password)
user.is_employee = True
user.save()
registered = True
else:
print("Error!")
else:
Employee_form = EmployeeForm()
return render(request,'Employee_registration_form.html',
{'Employee_form':Employee_form,
'registered':registered})
Could you please help me or give me direction to dive in ?
Thx you ;)
For activating/deactivating a user you could use:
user.is_active = False/True
For creating the user I recommend the following article:
https://medium.com/#ramykhuffash/django-authentication-with-just-an-email-and-password-no-username-required-33e47976b517

Django. Сheck activation code

UPDATE!
The activation code is stored in the database, but I can not call it as a function of activation
Gives an error message:
TypeError at /account/activation/18d2ecbee1fd15440bbcfdf942c071a2f5b8d0ff/
activation() got an unexpected keyword argument 'activation_key'
forms.py
from django import forms
from django.contrib.auth.models import User
class EmailForm(forms.Form):
email = forms.EmailField(widget=forms.EmailInput(
attrs={'class': 'form-control input-lg',
'placeholder': 'Ваш E-mail'}))
views.py
import datetime
import hashlib
import random
from django.conf import settings
from django.contrib.auth import authenticate, logout, login
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from django.http import HttpResponseRedirect, HttpResponse
from django.http import Http404
from django.shortcuts import render
from forms import *
from models import *
def register(request):
if request.method == "POST":
form = EmailForm(request.POST)
if form.is_valid():
form_email = form.cleaned_data['email']
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
activation_key = hashlib.sha1(salt+form_email).hexdigest()
key_expires = datetime.datetime.today() + datetime.timedelta(2)
subject = 'Activation your e-mail'
from_email = settings.EMAIL_HOST_USER
to_email = [form_email]
message_email = "Hi, i'm link activate e-mail! \
http://127.0.0.1:8000/account/activation/%s" % activation_key
send_mail(subject,
message_email,
from_email,
to_email,
fail_silently=True)
code = ActivationEmail.objects.create(activation_key=activation_key)
code.save()
return render(request, 'registration/activate.html', locals())
else:
form = EmailForm()
return render(request, 'registration/register.html', locals())
def activation(request, code):
try:
active = ActivationEmail.objects.get(activation_key=code)
except ActivationEmail.DoesNotExist:
active = None
active.validated = True
if not active:
raise Http404
print "USER WAS HERE?!"
return HttpResponseRedirect('/account/wizard/')
urls.py
from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns('',
url(r'^register/', views.register, name='registration'),
url(r'^activation/(?P<activation_key>\w+)/', views.activation, name='activation'),
url(r'^login/$', views.user_login, name='login'),
url(r'^logout/$', views.user_logout, name='logout'),
Just can not figure out how to do next; (
You will need to
1- You will need to create a new table to store the activation keys, such that you can create a new entry before sending the email
2.- On the user clicking the activation link, that link should be sufficient for you to find the record you created before sending the email
3.- If everything matches, then set a user.is_active type thing on your user model.
But all that said, you are reinventing the wheel here. There are several top notch packages you can use with this and more. I would recommend django-allauth with also give you social login support (e.g. facebook). If you just want the activation portion, there is an older package called django-registration. There are a few others if you search around, but the point is you don't need to implement this (and you probably don't want to mess around with registration if you are not an expert)

How do I connect admin page in django with custom auth backend?

I used a custom auth backend in my django project to connect users, my problem is that i'm not able to connect using the admin interface anymore.
this is my custom auth files :
auth_backends.py:
from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_model
class StudentModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = self.user_class_s.objects.get(username=username)
if user.check_password(password):
return user
except self.user_class_s.DoesNotExist:
return None
def get_user(self, user_id):
try:
return self.user_class_s.objects.get(pk=user_id)
except self.user_class_s.DoesNotExist:
return None
#property
def user_class_s(self):
if not hasattr(self, '_user_class'):
self._user_class = get_model(*settings.CUSTOM_USER_MODEL.split('.', 2))
if not self._user_class:
raise ImproperlyConfigured('Could not get student model')
return self._user_class
class ProfessorModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = self.user_class_p.objects.get(username=username)
if user.check_password(password):
return user
except self.user_class_p.DoesNotExist:
return None
def get_user(self, user_id):
try:
return self.user_class_p.objects.get(pk=user_id)
except self.user_class_p.DoesNotExist:
return None
#property
def user_class_p(self):
if not hasattr(self, '_user_class'):
self._user_class = get_model(*settings.CUSTOM_USER_MODEL_P.split('.', 2))
if not self._user_class:
raise ImproperlyConfigured('Could not get student model')
return self._user_class
settings.py:
AUTHENTICATION_BACKENDS = (
'authentification.auth_backends.StudentModelBackend',
'authentification.auth_backends.ProfessorModelBackend',
)
CUSTOM_USER_MODEL = 'forum.Student'
CUSTOM_USER_MODEL_P = 'forum.Professor'
I tried this solution :
from django.contrib.auth.decorators import login_required
admin.autodiscover()
admin.site.login = login_required(admin.site.login)
but it redirect me to the user auth interface instead of admin interface.
Could someone help me please ?
I Found the solution,
i just have to tell django to try connecting using the original backend if the user isnt a student or professor.
just add 'django.contrib.auth.backends.ModelBackend' in settings.py
settings.py :
AUTHENTICATION_BACKENDS = (
'authentification.auth_backends.StudentModelBackend',
'authentification.auth_backends.ProfessorModelBackend',
'django.contrib.auth.backends.ModelBackend',
)
The default login_required decorator will default to the settings.py LOGIN_REDIRECT_URL. However, you can also explicitly tell it which login view to use by passing the login_url keyword argument.
https://github.com/django/django/blob/stable/1.8.x/django/contrib/auth/decorators.py#L39
This may be used with the generic login_required view and my samples below show more precise conditionals by using the user_passes_test decorator.
For instance:
from django.contrib.auth.decorators import user_passes_test
def superuser_required(*args, **kwargs):
return user_passes_test(lambda u: u.is_superuser, login_url='admin:login')(*args, **kwargs)
def custom_login_required(*args, **kwargs):
return user_passes_test(lambda u: getattr(u, 'is_custom_user', False), login_url='custom-url-name-from-urls-py')(*args, **kwargs)
Then you should be able to use this new decorate as you have above with the generic login_required:
admin.site.login = custom_login_required(admin.site.login)