I have a Django project with a login screen; when I enter my username and password I get directed to the home page. This works fine! But nothing is secure, I can just go to the view at /allstudyplans without logging in and see all the information there. My Question is how do I make it so it's not possible to go /allstudyplans without logging in first?
Form
User = get_user_model()
class UserLoginForm(forms.Form):
username = forms.CharField(label='', widget=forms.TextInput(
attrs={
'class': 'form-control',
'autofocus': '',
'placeholder': 'Användarnamn',
}
), )
password = forms.CharField(label='', widget=forms.PasswordInput(
attrs={
'class': 'form-control mt-1',
'placeholder': 'Lösenord',
}
), )
# https://docs.djangoproject.com/en/2.1/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
def clean(self):
cleaned_data = super().clean()
username = cleaned_data.get('username')
password = cleaned_data.get('password')
if username and password:
user = authenticate(username=username, password=password)
if not user:
raise forms.ValidationError(
'Oh! I can\'t find that user - create user first!')
elif not user.check_password(password):
raise forms.ValidationError(
'Oh! That password is incorrect - try again!')
elif not user.is_active:
raise forms.ValidationError(
'Oh! That user is not active in the database!')
Views
def home(request):
next = (request.GET.get('next'))
form = UserLoginForm(request.POST or None)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
login(request, user)
if next:
return redirect(next)
return redirect('/')
context = {
'form': form,
}
return render(request, "utility-login.html", context)
def Assignments(request):
return render(request, 'nav-side-team.html')
def DetailAssignment(request):
obj = Assignment.objects.all()
context = {
'object': obj
}
return render(request, 'nav-side-project.html', context)
def studyplans(request):
return render(request, 'allStudyplans.html')
def detailStudyplan(request):
return render(request, 'detailStudyplan.html')
Also a Home View in the normal project file (not the app)
#login_required
def homee(request):
return render(request, "allstudyplans.html", {})
Urls in the project:
urlpatterns = [
path('admin/', admin.site.urls),
path('account/', include('accounts.urls')),
path('', include('accounts.urls'))
]
Urls in the app:
urlpatterns = [
path('', views.studyplans),
path('login', views.home),
path('nav-side-team.html', views.Assignments),
path('nav-side-project.html', views.DetailAssignment),
path('allstudyplans.html', views.studyplans),
path('detailStudyplan.html', views.detailStudyplan),
]
Tried this:
#login_required
def Assignments(request):
if not request.user.is_authenticated:
return redirect('%s?next=%s' % ('utility-login.html', request.path))
return render(request, 'nav-side-team.html')
studyplans is missing the decorator login_required.
According to the docs, https://docs.djangoproject.com/en/2.2/topics/auth/default/#the-login-required-decorator, the easiest way should be
#login_required
def studyplans(request):
return render(request, 'allStudyplans.html')
Another way to do it – probably equivalent to the above – is with "mixins":
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
class MyView(LoginRequiredMixin, View):
...
Notice that "mixin" class names should occur first in the list, before View.
(Notice that I also show that there's a "permission-required mixin.")
I guess it just comes down to personal preference, and consistency with the existing source-code of the application.
Related
I am creating a flask api with login auth but the check_password_hash ever return false and I get a error
in my app.py i'm trying this
from werkzeug.security import generate_password_hash, check_password_hash
#app.route("/signup", methods=["GET", "POST"])
def signup():
if request.method == "POST":
hashed_pw = generate_password_hash(request.form["password"], method="sha256")
new_user = Users(username=request.form["username"], password=hashed_pw)
db.session.add(new_user)
db.session.commit()
return "You've registered successfully."
return render_template("signup.html")
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
user = Users.query.filter_by(username=request.form["username"]).first()
if user and check_password_hash(user.password, request.form["password"]):
session['username'] = user.username
return "You are logged in"
else:
return "Your credentials are invalid, check and try again."
return render_template("login.html")
when i print user.password and request.form["password"] it returns hashed
pass -> sha256$SSC4jjZIE3Wm6l7v$74e78b19ddfa3ad62963c93f34d9c6cd93b67e47b4e42e896a726d79
pass -> 1
First make sure that request.form["password"] is returning the password that the user typed.
I don't know how you are hashing the password. Anyways a simple way to do it is using python passlib. it has no known weaknesses.
from passlib.hash import sha256_crypt
to save the hash:
hashed_password = sha256_crypt.hash("ThePassword")
to check the password:
ha256_crypt.verify("password from the form", hashed_password)
I'm unsure of how I can edit a foreign key's fields from the template of another model - my changes do not update the model on post, see below.
models
class Projects(models.Model):
name = models.CharField(max_length=250)
class Current(models.Model):
fk_user = models.OneToOneField(User, on_delete=models.CASCADE)
fk_project = models.ForeignKey(projects, default='1', on_delete=models.CASCADE)
views
class current(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Current
fields = [
'fk_project'
]
template_name = 'users/current.html'
context_object_name = 'current'
def form_valid(self, form):
form.instance.fk_user = self.request.user
form.save()
# return super().form_valid(form)
return HttpResponseRedirect(self.request.path_info)
def test_func(self):
post = self.get_object()
if self.request.user == post.fk_user:
return True
return False
current.html
<form method="POST">{% csrf_token %}
<input type="text" value="{{ current.fk_project.name }}"/>
<button type="submit">post</button>
I would suggest you amend form_valid to follow the usual conventions. No idea if this will fix it, but
def form_valid(self, form):
instance = form.save( commit=False)
instance.fk_user = self.request.user
instance.save()
# return super().form_valid(form)
return HttpResponseRedirect(self.request.path_info)
If the desire is to update fields in the Project which is identified by the form as well as to store it in the Current instance which is the subject of this UpdateView, you can accomplish this by adding appropriate non-Model fields to the form and then processing the data in form_valid. For example, to pick a project, link it as at present, and simultaneously update its name:
class CurrentAndProjectUpdateForm( forms.ModelForm):
class Meta:
model = Current
fields = ['fk_project', ]
name = forms.CharField( ...) # new name for the related project
class current(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Current # don't think this now necessary
form_class = CurrentAndProjectUpdateForm # because it's implicit in this form
# fields = # DEFINITELY not needed here now
...
def form_valid(self, form):
instance = form.save( commit=False)
instance.fk_user = self.request.user
instance.save()
project = instance.fk_project
project.name = form.cleaned_data['name']
project.save()
# return super().form_valid(form)
return HttpResponseRedirect(self.request.path_info)
I have been trying to add a friend system to django in which the user can add and remove friends, in the process I found a Friend matching query does not exist error and shows that this part of the code is bad:
friend = Friend.objects.get(current_user=request.user)
now here I will leave the complete code.
views.py
def profile(request, username=None):
friend = Friend.objects.get(current_user=request.user)
friends = friend.users.all()
if username:
post_owner = get_object_or_404(User, username=username)
user_posts=Post.objects.filter(user_id=post_owner)
else:
post_owner = request.user
user_posts=Post.objects.filter(user=request.user)
args1 = {
'post_owner': post_owner,
'user_posts': user_posts,
'friends': friends,
}
return render(request, 'profile.html', args1)
def change_friends(request, operation, pk):
friend = User.objects.get(pk=pk)
if operation == 'add':
Friend.make_friends(request.user, friend)
elif operation == 'remove':
Friend.lose_friends(request.user, friend)
return redirect('profile')
models.py
class Friend(models.Model):
users = models.ManyToManyField(User, default='users', blank=True, related_name='users')
current_user = models.ForeignKey(User, related_name='owner', on_delete=models.CASCADE, null=True)
#classmethod
def make_friend(cls, current_user, new_friend):
friend, created = cls.objects.get_or_create(
current_user=current_user
)
friend.users.add(new_friend)
#classmethod
def lose_friend(cls, current_user, new_friend):
friend, created = cls.objects.get_or_create(
current_user=current_user
)
friend.users.remove(new_friend)
if the profile.html is neede please let me know:)
request.user has no Friend record as the error indicates. You can simple change the get operation to filter&first operations. Friend.objects.filter(current_user=request.user).first() then do not forget to check friend instance exists. So your view should be something like:
def profile(request, username=None):
friend = Friend.objects.filter(current_user=request.user).first()
friends = []
if friend:
friends = friend.users.all()
if username:
post_owner = get_object_or_404(User, username=username)
user_posts = Post.objects.filter(user_id=post_owner)
else:
post_owner = request.user
user_posts = Post.objects.filter(user=request.user)
args1 = {
'post_owner': post_owner,
'user_posts': user_posts,
'friends': friends,
}
return render(request, 'profile.html', args1)
I'm setting up Django to send a JWT Response as opposed to a view. I tried using django-rest-framework-simplejwt.
Provided in this framework, there is a function TokenObtainPairView.as_view() that returns a pair of jwt. I need to return the access token with another Json response as opposed to the two tokens provided.
Ideally I would like one JsonResponse that contains an access token that is the same as this one: TokenObtainPairView.as_view().
I tried creating my own view which is provided below.
UPDATE: Provided in Settings.py
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(days=1),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
Login URL Path
urlpatterns = [
path('auth/', views.LoginView.as_view()),
]
LoginView I created
class LoginView(APIView):
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
username = request.data['username']
password = request.data['password']
user = authenticate(username=username, password=password)
if user is not None:
payload = {
'user_id': user.id,
'exp': datetime.now(),
'token_type': 'access'
}
user = {
'user': username,
'email': user.email,
'time': datetime.now().time(),
'userType': 10
}
token = jwt.encode(payload, SECRET_KEY).decode('utf-8')
return JsonResponse({'success': 'true', 'token': token, 'user': user})
else:
return JsonResponse({'success': 'false', 'msg': 'The credentials provided are invalid.'})
Pattern provided by framework.
urlpatterns = [
...
path('token/', TokenObtainPairView.as_view()),
...
]
It returns this token
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ5NDk3NDQ2LCJqdGkiOiI3YmU4YzkzODE4MWI0MmJlYTFjNDUyNDhkNDZmMzUxYSIsInVzZXJfaWQiOiIwIn0.xvfdrWf26g4FZL2zx3nJPi7tjU6QxPyBjq-vh1fT0Xs
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU0OTQ5NzQ0NiwianRpIjoiOTNhYzkxMjU5NmZkNDYzYjg2OGQ0ZTM2ZjZkMmJhODciLCJ1c2VyX2lkIjoiMCJ9.dOuyuFuMjkVIRI2_UcXT8_alCjlXNaiRJx8ehQDIBCg
If you go to https://jwt.io/ you will see what's returned
For example: to customize simpleJWT response by adding username and groups,
Override the validate method in TokenObtainPairSerializer
# project/views.py
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
# Add extra responses here
data['username'] = self.user.username
data['groups'] = self.user.groups.values_list('name', flat=True)
return data
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
replace the login view with customized view
# project/urls.py
from .views import MyTokenObtainPairView
urlpatterns = [
# path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
]
🔚
References: SimpleJWT Readme and the source code as below:
A very clean approach from the django-rest-framework-simplejwt README as below
For example, I have users app where my custom User model resides. Follow serializers and views code example below.
users/serializers.py:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token['name'] = user.name
# Add more custom fields from your custom user model, If you have a
# custom user model.
# ...
return token
users/views.py:
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
After that, Register above View in your project's urls.py replacing original TokenObtainPairView as below.
from users.views import MyTokenObtainPairView
urlpatterns = [
..
# Simple JWT token urls
# path('api/token/', TokenObtainPairView.as_view(),
# name='token_obtain_pair'),
path('api/token/', CustomTokenObtainPairView.as_view(),
name='token_obtain_pair')
..
]
Now get access token and decode it at jwt.io
Custom Token response like this:
from rest_framework_simplejwt.tokens import RefreshToken
class LoginView(APIView):
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
# ...
# Get user token, include refresh and access token
token = RefreshToken.for_user(user)
# Serializer token if it is not correct JSON format ^^
return JsonResponse({'success': 'true', 'token': token, 'user': user})
I had the same issue this can be resolved by something like this if you want to handle the custom response in case of an invalid credential.
# JWT View
class LoginView(TokenObtainPairView):
'''
Returns access token and refresh token on login
'''
permission_classes = (AllowAny,)
serializer_class = MyTokenObtainPairSerializer
def post(self, request, *args, **kwargs):
try:
response = super().post(request, *args, **kwargs)
return response
except exceptions.AuthenticationFailed:
return Response({
'error': True,
'message': 'Invalid Username or Password',
}, status=status.HTTP_401_UNAUTHORIZED)
So in Django, i have a base template which has some contact details in it. but every view that i generate i have to have the line.
contact = Contact.objects.first()
Then i have to add that object to the dictionary that's loaded with the template.
What is the better way to deal with is? I find it hard to believe that i'm doing it in the correct way.
Examaple views.py
from django.shortcuts import render
from services.models import Service, ServicesDetail
from .models import Feature, CompanyDetail, TeamMember, TeamDetail, Banner
from contact.models import ContactDetail
import json
# Create your views here.
def home(request):
services = Service.objects
try:
overview = ServicesDetail.objects.first()
except ServicesDetail.DoesNotExist:
overview = ''
try:
company = CompanyDetail.objects.first()
except CompanyDetail.DoesNotExist:
company = ''
features = Feature.objects
contact_details = ContactDetail.objects.first()
banners = Banner.objects
return render(request, 'home.html', {'overview': overview,
'services': services,
'company': company,
'features': features,
'contact_detail': contact_details,
'banners': banners})
def company(request):
services = Service.objects
try:
company = CompanyDetail.objects.first()
except CompanyDetail.DoesNotExist:
company = ''
features = Feature.objects
contact_details = ContactDetail.objects.first()
return render(request, 'company.html', {'services': services,
'company': company,
'features': features,
'contact_detail': contact_details,})
def team(request):
services = Service.objects
members = TeamMember.objects
try:
teampage = TeamDetail.objects.first()
except TeamDetail.DoesNotExist:
teampage = ''
contact_details = ContactDetail.objects.first()
return render(request, 'team.html', {'services': services,
'members': members,
'teampage': teampage,
'contact_detail': contact_details,})
you can switch to class base template views and write your custom base class
class MyBaseTemplateView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['contact_details'] = ContactDetail.objects.first()
return context
class MyActualView(MyBaseTemplateView):
template_name = 'company.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# set other specific context values for this view here
return context
and add these views to your urls.py like this:
urlpatterns = [
path('', MyActualView.as_view(), name='myactualview'),
]
You don't need to do it in each view, just write custom context processor:
def contact_details(request):
return {'contact_detail': contact_details = ContactDetail.objects.first()}
And add it to TEMPLATES setting:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'path.to.processor.contact_details'
],
},
},
]