plain.password
Password authentication for Plain.
Usage
To enable password authentication in your Plain application, add the PasswordLoginView
to your urls.py
:
# app/urls.py
from plain.urls import path
from plain.passwords.views import PasswordLoginView
urlpatterns = [
path('login/', PasswordLoginView.as_view(), name='login'),
# ...
]
This sets up a basic login view where users can authenticate using their username and password.
FAQs
How do I customize the login form?
To customize the login form, you can subclass PasswordLoginForm
and override its fields or methods as needed. Then, set the form_class
attribute in your PasswordLoginView
to use your custom form.
# app/forms.py
from plain.passwords.forms import PasswordLoginForm
class MyCustomLoginForm(PasswordLoginForm):
# Add custom fields or override methods here
pass
# app/views.py
from plain.passwords.views import PasswordLoginView
from .forms import MyCustomLoginForm
class MyPasswordLoginView(PasswordLoginView):
form_class = MyCustomLoginForm
Update your urls.py
to use your custom view:
# app/urls.py
from plain.urls import path
from .views import MyPasswordLoginView
urlpatterns = [
path('login/', MyPasswordLoginView.as_view(), name='login'),
# ...
]
1# Avoid shadowing the login() and logout() views below.
2from plain.auth.sessions import USER_HASH_SESSION_KEY, get_session_auth_hash
3from plain.auth.sessions import login as auth_login
4from plain.http import (
5 ResponseRedirect,
6)
7from plain.views import CreateView, FormView
8
9from .forms import (
10 # PasswordChangeForm,
11 # PasswordResetForm,
12 # SetPasswordForm,
13 PasswordLoginForm,
14 PasswordSignupForm,
15)
16
17
18def update_session_auth_hash(request, user):
19 """
20 Updating a user's password logs out all sessions for the user.
21
22 Take the current request and the updated user object from which the new
23 session hash will be derived and update the session hash appropriately to
24 prevent a password change from logging out the session from which the
25 password was changed.
26 """
27 request.session.cycle_key()
28 if request.user == user:
29 request.session[USER_HASH_SESSION_KEY] = get_session_auth_hash(user)
30
31
32# Class-based password reset views
33# - PasswordResetView sends the mail
34# - PasswordResetDoneView shows a success message for the above
35# - PasswordResetConfirmView checks the link the user clicked and
36# prompts for a new password
37# - PasswordResetCompleteView shows a success message for the above
38
39
40# class PasswordContextMixin:
41# extra_context = None
42
43# def get_template_context(self):
44# context = super().get_template_context()
45# context.update(
46# {"title": self.title, "subtitle": None, **(self.extra_context or {})}
47# )
48# return context
49
50
51# class PasswordResetView(PasswordContextMixin, FormView):
52# email_template_name = "auth/password_reset_email.html"
53# extra_email_context = None
54# form_class = PasswordResetForm
55# from_email = None
56# html_email_template_name = None
57# subject_template_name = "auth/password_reset_subject.txt"
58# success_url = reverse_lazy("password_reset_done")
59# template_name = "auth/password_reset_form.html"
60# title = "Password reset"
61# token_generator = default_token_generator
62
63# def form_valid(self, form):
64# opts = {
65# "use_https": self.request.is_https(),
66# "token_generator": self.token_generator,
67# "from_email": self.from_email,
68# "email_template_name": self.email_template_name,
69# "subject_template_name": self.subject_template_name,
70# "html_email_template_name": self.html_email_template_name,
71# "extra_email_context": self.extra_email_context,
72# }
73# form.save(**opts)
74# return super().form_valid(form)
75
76
77# INTERNAL_RESET_SESSION_TOKEN = "_password_reset_token"
78
79
80# class PasswordResetDoneView(PasswordContextMixin, TemplateView):
81# template_name = "auth/password_reset_done.html"
82# title = "Password reset sent"
83
84
85# class PasswordResetConfirmView(PasswordContextMixin, FormView):
86# form_class = SetPasswordForm
87# post_reset_login = False
88# post_reset_login_backend = None
89# reset_url_token = "set-password"
90# success_url = reverse_lazy("password_reset_complete")
91# template_name = "auth/password_reset_confirm.html"
92# title = "Enter new password"
93# token_generator = default_token_generator
94
95# def get_response(self):
96# if "uidb64" not in self.url_kwargs or "token" not in self.url_kwargs:
97# raise ImproperlyConfigured(
98# "The URL path must contain 'uidb64' and 'token' parameters."
99# )
100
101# self.validlink = False
102# self.user = self.get_user(self.url_kwargs["uidb64"])
103
104# if self.user is not None:
105# token = self.url_kwargs["token"]
106# if token == self.reset_url_token:
107# session_token = self.request.session.get(INTERNAL_RESET_SESSION_TOKEN)
108# if self.token_generator.check_token(self.user, session_token):
109# # If the token is valid, display the password reset form.
110# self.validlink = True
111# response = super().get_response()
112# add_never_cache_headers(response)
113# return response
114# else:
115# if self.token_generator.check_token(self.user, token):
116# # Store the token in the session and redirect to the
117# # password reset form at a URL without the token. That
118# # avoids the possibility of leaking the token in the
119# # HTTP Referer header.
120# self.request.session[INTERNAL_RESET_SESSION_TOKEN] = token
121# redirect_url = self.request.path.replace(
122# token, self.reset_url_token
123# )
124# response = ResponseRedirect(redirect_url)
125# add_never_cache_headers(response)
126# return response
127
128# # Display the "Password reset unsuccessful" page.
129# response = self.render_to_response(self.get_template_context())
130# add_never_cache_headers(response)
131# return response
132
133# def get_user(self, uidb64):
134# UserModel = get_user_model()
135# try:
136# # urlsafe_base64_decode() decodes to bytestring
137# uid = urlsafe_base64_decode(uidb64).decode()
138# user = UserModel._default_manager.get(pk=uid)
139# except (
140# TypeError,
141# ValueError,
142# OverflowError,
143# UserModel.DoesNotExist,
144# ValidationError,
145# ):
146# user = None
147# return user
148
149# def get_form_kwargs(self):
150# kwargs = super().get_form_kwargs()
151# kwargs["user"] = self.user
152# return kwargs
153
154# def form_valid(self, form):
155# user = form.save()
156# del self.request.session[INTERNAL_RESET_SESSION_TOKEN]
157# if self.post_reset_login:
158# auth_login(self.request, user, self.post_reset_login_backend)
159# return super().form_valid(form)
160
161# def get_template_context(self):
162# context = super().get_template_context()
163# if self.validlink:
164# context["validlink"] = True
165# else:
166# context.update(
167# {
168# "form": None,
169# "title": "Password reset unsuccessful",
170# "validlink": False,
171# }
172# )
173# return context
174
175
176# class PasswordResetCompleteView(PasswordContextMixin, TemplateView):
177# template_name = "auth/password_reset_complete.html"
178# title = "Password reset complete"
179
180# def get_template_context(self):
181# context = super().get_template_context()
182# context["login_url"] = resolve_url(settings.AUTH_LOGIN_URL)
183# return context
184
185
186# class PasswordChangeView(PasswordContextMixin, AuthViewMixin, FormView):
187# form_class = PasswordChangeForm
188# success_url = reverse_lazy("password_change_done")
189# template_name = "auth/password_change_form.html"
190# title = "Password change"
191# login_required = True
192
193# def get_form_kwargs(self):
194# kwargs = super().get_form_kwargs()
195# kwargs["user"] = self.request.user
196# return kwargs
197
198# def form_valid(self, form):
199# form.save()
200# # Updating the password logs out all other sessions for the user
201# # except the current one.
202# update_session_auth_hash(self.request, form.user)
203# return super().form_valid(form)
204
205
206# class PasswordChangeDoneView(PasswordContextMixin, AuthViewMixin, TemplateView):
207# template_name = "auth/password_change_done.html"
208# title = "Password change successful"
209# login_required = True
210
211
212class PasswordLoginView(FormView):
213 form_class = PasswordLoginForm
214 success_url = "/"
215
216 def get(self):
217 # Redirect if the user is already logged in
218 if self.request.user:
219 return ResponseRedirect(self.success_url)
220
221 return super().get()
222
223 def form_valid(self, form):
224 # Log the user in and redirect
225 auth_login(self.request, form.get_user())
226
227 return super().form_valid(form)
228
229
230class PasswordSignupView(CreateView):
231 form_class = PasswordSignupForm
232 success_url = "/"
233
234 def form_valid(self, form):
235 # # Log the user in and redirect
236 # auth_login(self.request, form.save())
237
238 return super().form_valid(form)