My scenario:
My app, handles signup at /profile/signup/ using SingupView(FormView).
class SignupView(FormView):
template_name = "profile/signup.html"
template_name_signup_confirmed = "profile/created.html"
template_name_inactive = "profile/inactive.html"
def form_valid(self, form):
# Create and save inactive user
self.created_user = self.create_user(form, commit=False)
self.created_user.is_active = False
self.created_user.save()
# Create and save user's profile
profile = self.create_profile(form)
# Send registration email
self.send_registration_email([self.created_user.email], registration_code=profile.registration_code)
# Response
return render_to_response(self.template_name_signup_confirmed, {'email': self.created_user.email})
def form_invalid(self, form):
if 'email' in form.errors and form.errors['email'].as_text() == \
'* An inactive user is registered with this email address.':
return render_to_response(self.template_name_inactive, {'email': form.data["email"]})
return render_to_response(self.template_name, self.get_context_data(form=form))
In SingupView().form_valid(form) the User and his Profile are created, user is signed up, but inactive.
After that, in my case there is not success url to redirect, but render a new page, at the same address (/profile/signup/) with a new html saying "An email was sent at youremail#email.com, check and activate".
The same case when an inactive, registered user, tries to signup again, he will get a new page, rendered at the same adress /profile/signup/ saying 'Your email youremail#email.com is already in our db but is not active.. .'
My questions:
Can anyone explains if this is a good way to go, or I need to redirect to new urls, controlled by a new views?
Is there any security risk by using render instead of redirect? Especially on user sign in/sign up?
What is the difference when using redirect or render a new template at the same address? What is the best practice in Django?
Is it ok, to display user's email, in the signup confirmation page and also in the alert page that says that user is registered but inactive in db?
The common pattern for this sort of behaviour is to use a redirect.
I would personally prefer that as it avoid the confuson of one view doing two things - one view displays and processes the form, the other displays a success message.
If the user manages to POST the form data a second time, what do you do?
https://en.wikipedia.org/wiki/Post/Redirect/Get
I am not aware of any greater security risk from using render rather than redirect (though someone with more expertise may know more than me about that).
Related
I have made a custom user model using the AbstractUser , removed the username and replaced it with email and extended the model, tried creating superuser and it worked and also created some users by a signup form , logged in the admin interface and it worked however when tried to create a login form for the users it fails
I tried this but it didn't work
def LoginView(request):
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
if form.is_valid():
user = form.get_user()
login(request,user)
return redirect('accounts:login')
else:
form = AuthenticationForm()
return render(request,'accounts/login.html', {'form':form})
then i tried this
class LoginView(FormView):
form_class = AuthenticationForm
template_name = 'login.html'
def form_valid(self, form):
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(email=email, password=password)
# Check here if the user is an admin
if user is not None and user.is_active:
login(self.request, user)
return HttpResponseRedirect(self.success_url)
else:
return self.form_invalid(form)
Obviously i expect the user to be logged in
i think the code in this post is badly formatted. mainly it's my fault as i'm new to this platform
I've developed almost the same setup that you're describing (I didn't remove the username field, just stopped using it). If you haven't already seen it, Django's documentation at https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#substituting-a-custom-user-model is quite helpful.
There are a couple of important things that need to be set up correctly for this to work.
The USERNAME_FIELD on your model should be set to the name of your email field.
The AUTH_USER_MODEL needs to point to your custom user model.
class MyUser(AbstractUser):
USERNAME_FIELD = 'email'
AUTH_USER_MODEL = 'customauth.MyUser'
Since you've removed the username field altogether you might need to subclass django.contrib.auth.forms.AuthenticationForm and django.contrib.auth.views.LoginView to avoid breaking things, but Django should handle a different authentication field quite well.
If you do wind up needing to subclass the view, https://ccbv.co.uk/projects/Django/2.2/django.contrib.auth.views/LoginView/ is a great place to look over all the methods to see what's going on.
Edit - On Subclassing and it's necessity
What I was saying about possibly needing to subclass certain things was influenced by https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#writing-a-manager-for-a-custom-user-model. I wasn't sure if there were other parts of the authentication system that would need you to customize them because you removed the username field.
I've read through some of the source code for Django's authentication system. Here's the path that's being followed.
When the POST request is made to Django's authentication view the authentication form is validated. https://github.com/django/django/blob/2.2.2/django/contrib/auth/forms.py#L191
The authenticate function is called. This iterates through the backends set up and tries to authenticate on each of them. https://github.com/django/django/blob/2.2.2/django/contrib/auth/__init__.py#L62
Django's built-in authentication backend gets the user if it exists using the natural key. https://github.com/django/django/blob/2.2.2/django/contrib/auth/backends.py#L16
We can see in the base manager that the natural key used is the field named by USERNAME_FIELD. https://github.com/django/django/blob/2.2.2/django/contrib/auth/base_user.py#L43
If the form is valid, meaning that the user is authenticated properly, the user is then logged in. https://github.com/django/django/blob/2.2.2/django/contrib/auth/views.py#L88
My reaction is that it looks like Django should work out of the box for your use case. You shouldn't need to write a backend. Here's the extent of the code my gut says you should have to write.
from django.contrib.auth import views as auth_views
from django.shortcuts import resolve_url
class LoginView(auth_views.LoginView):
template_name = 'accounts/login.html'
def get_success_url(self):
return resolve_url('accounts:login')
I don't know if this could be of any use to somebody but I can confirm that Django can authenticate you well with its own Login view if you just replace the username with an email field on your custom user model (as long as you specify the USERNAME_FIELD on the custom user model and are indeed using it by declaring it in the settings).
As I was expecting this behavior I designed a custom HTML form with email/password inputs and used the same principles I would use with the original user model authentication. It was failing though and I understood it was because I wasn't adapting my form to the original Login view expectations or would have worked from the start.
Just make sure to remember that the form HTML input tag for the email address needs to have "type" set to "email" but "id" set to "id_username" and "name" to "username".
This means you can just replace username with an email field and authenticate normally. I did not even declare a Login view for my login form to work, the Django view automacally used from the core is just being called at /accounts/login and working on its own. I'm working on Django 3.2
I have my first project as junior in my work. It is old (Django 1.8) and it is normal django framework... not REST.
It supports web, and mobile.
I have to create endpoint for mobile to create user.
I think it is not a problem (to create) but I want to make sure it will be save.
First of all I thought that I will create normal ModelForm (RegisterAPIForm based on model=User with full validation (I mean init all fields that are in "backend" not visible for user | cleaned_data for all fields | special overwriten method save() that in addition hashes password, and send email) and in Views I'll add something like this:
class RegistrationAPITestView(View):
def post(self, request):
form = RegistrationAPIForm(
request.POST
)
if form.is_valid():
form.save()
return JsonResponse({})
else:
#check errors and send error code back
Or I should do it other way, by using User object?
class RegistrationAPITestView(View):
def post(self, request):
#check if user does not exist
#password1 and password2 validation
user = User.objects.create()
user.name = request.POST['username']
user.set_password(request.POST['password'])
#init all fields that user can't choose like groups etc
user.save()
What do you think? Do I need ModelForm that I won't even render? It seems to be safer, but maybe I should check it other way? with User object?
Btw. Registration form already exists for web but there is a lot of "web" stuff that I don't need and I don't have to check and there is another method of saving password, so I believe I should create new one.
My code will be revieved in 2 weeks (senior vacations) but now I'm alone and want to do my best.
There is nothing wrong with the second option, but here is the problem that you as a junior should avoid. This line will make the server return a 500 error request.POST['username'] because python will throw a key error if the user doesn't provide the username, to fix just change to request.POST.get('username', 'value if doesn\'t exit') also make sure that everything is ready before create the user or you will have records in the database that wont be useful. Call validators to the password too and try to cover all possible scenario. Remember never trust the user
I am writing a web app using Django. I am trying to allow a user to see its profile and only his own.
if(not request.user.id == request.GET.get('user_id', '')):
raise PermissionDenied
My question is: is it safe to check this way or is it possible for a smart kid to somehow alter the value in request.user.id to match the user_id of anyone?
The user must be logged in before accessing this page using this:
user = LDAPBackend().authenticate(username=username, password=password)
if(user is not None):
login(request, user)
Yes it should be safe.
request.user get's only populated when authentication with session cookies. Unless and until someone steals the cookie or token it should be no issue.
One thing i don't understand is why do you need user_id parameter here to be explicitly passed.
if you are putting logged in compulsory to view the page. there are two way i can see this.
/profile
Directly get user profile corresponding to the request.user
/<username>
Query the profile corresponding to the username and compare it with request.user.id
request.user is set using AuthenticationMiddleware for each request:
Adds the user attribute, representing the currently-logged-in user, to every incoming HttpRequest object.
If a user is not logged in then request.user is set to Anonymous User. Have a look at Authentication in Web requests.
So, I am not sure how would a smart kid alter the id of the logged-in user.
Mostly, there is a one-to-one relation between the user and its profile. If that's the case you can modify the queryset to get the profile for request.user directly.
request.user is already an object about the current user who send the request to get the page. You can use login_required or to only allow user login to access (2 solutions : decorator or Mixin).
And then you can use your condition to load the page in the function. Example:
=> url.py:
url(r'^profile/$', login_required(app.views.profile), name='profile'),
=> views.py :
def profile(request):
try:
myProfile = User.objects.get(username=request.user.username)
except ObjectDoesNotExist:
return render(request, "error.html", {'message' : 'No Profile Found'})
return render(request, "app/profile.html",
{'myProfile': myProfile})
Like this you can only display YOUR profile (user who send the request) AND you need to be logged.
EDIT: if you don't want "try and catch" you can use get_object_or_404(User, username=request.user.username)
I understand how to log a user in/out as well as authenticate within django, but one thing that is mission critical to a new project of mine.
I would like to have the user logged in (which I have), and I would like to then ask the user for their credentials again on certain pages.
I have one method through a EmployeeAuthenticatedMixin that I have made, which checks the POST data for the credentials. The main problem is the Mixin does not redirect, it merely serves up a page. So a user can hit the refresh button and resubmit the form, giving them access again.
Is there any way to ask for the user credentials and allow them access to the next page? Maybe an internal Django thing? Sessions? Messages?
You can log them out forcing them to log back in, using request(logout)
pseudo-coded
def confirm_crednetials(request)
logout(request)
render 'form'
or First prompt the user with a form if they do not have a cookie, you can check and set the cookie with this built in django method resp.set_cookie(foo, cookie) but after you authenticate the user.
if 'id' in request.COOKIES:
**render page
else:
authenticate_user(username=foo, password=bar)
resp.set_cookie(foo, cookie)
I wrote a signal that would fire after login:
from django.contrib.auth.signals import user_logged_in
import datetime
def reauthentication(sender, user, request, **kwargs):
request.session['last_login_time'] = str(datetime.datetime.now())
request.session.save()
user_logged_in.connect(reauthentication)
Then I wrote middleware to catch views that require reauthentication if the sessions last_login_time is older than 3 minutes.
Hi I used the django inbult auth urls and views for my project and now have finished the initial user account creation/login/reset password process.
Now, the user can log in and be redirected to the after successful login url accounts/profile/.
I have several doubts on the django login function. For convenience, I've copy paste the django inbuilt login function code below.
#sensitive_post_parameters()
#csrf_protect
#never_cache
def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME,
authentication_form=AuthenticationForm,
current_app=None, extra_context=None):
"""
Displays the login form and handles the login action.
"""
redirect_to = request.REQUEST.get(redirect_field_name, '')
if request.method == "POST":
form = authentication_form(request, data=request.POST)
if form.is_valid():
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
# Okay, security check complete. Log the user in.
auth_login(request, form.get_user())
return HttpResponseRedirect(redirect_to)
else:
form = authentication_form(request)
current_site = get_current_site(request)
context = {
'form': form,
redirect_field_name: redirect_to,
'site': current_site,
'site_name': current_site.name,
}
if extra_context is not None:
context.update(extra_context)
return TemplateResponse(request, template_name, context,
current_app=current_app)
My questions are:
1 Is the REDIRECT_FIELD_NAME in the function set as '/profile/' in django.contrib.auth ?
I could see this variable is imported from django.contrib.auth
from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login, logout as auth_logout, get_user_model
I don't have any setting for this variable, but after user successfully logged in, the page will be directed to /accounts/profile/
2 Has the login function passed the account info about the user? If yes, how can I access it?
From the code, if user successfully logged in, page will be redirected: return HttpResponseRedirect(redirect_to)
in my case, redirected to accounts/profile/ , initially the view for the url was simply a
HttpResponse("You have logged in successfully")
now when I am trying to implement the view function, I realize that no info about the user has been passed.
I've tried to print request in the view function, but there is no info about the user in the message printed in the server terminal, all I get is a long list of system settings or other info. However, the login should pass the info of who has just successfully logged in to the successful log in urls right?
Thank you very much for explaining.
After the login, you can access the user info by referring request.user in views and just {{user}} in templates. All you need to make sure is you're passing the RequestContext in the HttpResponse for the future request.
Yes, REDIRECT_FIELD_NAME is defined in __init__.py of django.contrib.auth which is simply a "next" what you passed from the login form.
In Django, there are more than one ways to force a user to login. By decorating a view function with #login_required, by calling the build-in login view for an user defined URL and etc., Refer about the login settings variables here. You'll get some more ideas.
Building custom login page. That link gives you an example for custom login implementaion. Consider you have decorated a view with #login_required and it's corresponding URL is /login_test/. Then the {{next}} context variable in the login form will be rendered with /login_test/. So after you login,
<input type="hidden" name="next" value="{{ next }}" />
This element's value will be taken for redirecting as per the REDIRECT_FIELD_NAME. Though I suspect that that example is missing the setting of settings.LOGIN_URL to the URL login/. Never mind, it's being passed as an argument in the decorator itself.
To override this behavior just put following in settings.py of your app :
LOGIN_REDIRECT_URL = "/"
This will redirect to your home page. You can change this url to preferred url.
Once the user is redirected to accounts/profile/ the view for that link will be returned. You can access information about the currently logged in user there as per this post by using request.user. Also tip to see what information you have access to in your views. Use import pbd; pdb.set_trace(). This pops you into a python prompt with access to all of the current variables. To see all the defined variables call locals(), though this will print out a ton of junk along with it. In the template you can display a "you can't access this page" message if the user isn't logged in.