why django inbuilt auth views not recognizing the customized form? - python

I understand that when we need to make the django inbuilt views, parameter specification should be made before the inbuilt view function could use it.
now I want to customize the form for django auth viewpassword_reset_confirm
and in the url, I import my customized form
from Myapp.forms import PasswordSetForm
from django.contrib.auth import urls,views
and for the url
url(r'^accounts/ ^reset/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$ ',
'django.contrib.auth.views.password_reset_confirm',{'set_password_form':PasswordSetForm},
name='password_reset_confirm'),
url(r'^accounts/', include('django.contrib.auth.urls')),
in my form.py i import the original SetPasswordForm that is used as default form by django password_reset_confirm funciton
from django.contrib.auth.forms import SetPasswordForm
and then customize the form
class PasswordSetForm(SetPasswordForm):
error_messages = {
'invalid_password': _("Please enter a valid password as instructed"),
'password_mismatch': _("The two password fields didn't match."),
}
#new_password1 = forms.CharField(label=_("New password"),
# widget=forms.PasswordInput)
#new_password2 = forms.CharField(label=_("New password confirmation"),
# widget=forms.PasswordInput)
new_password1 = forms.CharField(widget=forms.PasswordInput, min_length=6, label='New Password' )
new_password2 = forms.CharField(widget=forms.PasswordInput, min_length=6, label='Confirm new password')
def clean_new_password1(self):
new_password1 = self.cleaned_data.get("new_password1")
# password must contain both Digits and Alphabets
# password cannot contain other symbols
if new_password1.isdigit() or new_password1.isalpha() or not new_password1.isalnum():
raise forms.ValidationError(
self.error_messages['invalid_password'],
code='invalid_password',
)
return new_password1
as you could see more checking are done for the new_password1
But after trying many times the page is still using the default SetpasswordForm as the default labels for the two password is displayed in my html instead of my customized label(in the html {{form.new_password2.label}} is used to display label) and no additional checking on the new_password1 is done
I have tried to create another MyPasswordSetForm that does not inherit the SetPassordForm and pass this to the password_reset_confirm but it does not make any difference, the default form is still used.
I have googled lot and ask questions on this, seems this is the right way to go, but What could be the problem here?
Thank you very much for helping.

Oops, that URL regexp looks wrong wrong wrong:
r'^accounts/ ^reset/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$ '
It should be:
r'^accounts/reset/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$'

Related

how to use PasswordChangeView django 2.2

I've to try make a change password page with Django.contrib.auth.views.PasswordChangeView like:
[views.py]
class ChangePasswordView(LoginRequiredMixin, PasswordChangeView):
template_name = 'accounts/password_change.html'
success_url = reverse_lazy('account:password_change_done')
class ChangePasswordDoneView(LoginRequiredMixin, PasswordChangeDoneView):
template_name = 'accounts/password_change_done.html'
With URL like:
[urls.py]
path('profile/update/password/', views.ChangePasswordView.as_view(),
name='password_change'),
path('profile/update/password/success/', views.ChangePasswordDoneView.as_view(),
name='password_change_done'),
I use custom user, but I look the source code, Django get the user by request.user so its not be a problem if I use the custom user.
But why my change password not work, if I change the password with right old password and new password the form return the success page but not changing my password and I try to use wrong password in old password but always return the success page. What's the problem?
iam so sory, my problem is in my form action not to password_change url but in my success url. my fail not check this. thanks

How to pass hidden variable to a form in django so it could be validated?

I have a registration form that goes through all the usual stuff, but with one bot prevention thing. I made a model SecurityQuestion, that consists of two charfields, Question and Answer. During registration one of them is randomly picked and is supposed to pass an answer to the form so it can be validated there. However, for the reason I'm yet to figure out, it doesn't seem to be passing the answer to the form
So let's start with the code
profile/forms.py
# FORM: Register an account
class UserRegistrationFirstForm(forms.ModelForm):
username = forms.CharField(max_length=20)
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
email = forms.EmailField()
email_confirm = forms.EmailField()
answer = forms.CharField(max_length=50, required=True)
hidden_answer = forms.CharField(widget=forms.HiddenInput)
class Meta:
model = User
fields = [
'username',
'email',
'email_confirm',
'password',
'password_confirm',
'answer',
]
def clean_answer(self):
formated_user_answer = self.cleaned_data.get("answer").lower()
formated_hidden_answer = self.cleaned_data.get("hidden_answer").lower()
if formated_hidden_answer != formated_user_answer:
raise forms.ValidationError("Incorect answer to security question!")
return answer
As you can see, there are two fields, answer and hidden_answer. answer is where users types in their answers, and hidden_answer is supposed to be populated when initializing form and passing init.
profiles/views.py
# VIEW: Register an account
def custom_register(request):
if request.user.is_authenticated():
return redirect(reverse('profile', host='profiles'))
# Grab a random registration security Q&A
qa_count = SecurityQuestion.objects.count() - 1
sec_qa = SecurityQuestion.objects.all()[randint(0, qa_count)]
# Form for users to register an account
form = UserRegistrationForm(request.POST or None, initial={"hidden_answer": sec_qa.answer,})
# Validate the registration form
if form.is_valid():
# Create new account
user = form.save(commit=False)
password = form.cleaned_data.get("password")
user.set_password(password)
user.save()
# Set the default avatar
user.profile.avatar = get_default_avatar()
user.profile.save()
login(request, user)
messages.success(request, "Welcome " + user.username + ", you have successfully registered an account!")
return redirect(reverse('pages:frontpage', host='www'))
# Context dict to return for template
context = {
"title": "Registration",
"form": form,
"question": sec_qa.question,
}
return render(request, 'profiles/register.html', context)
Alright, so in registration view, I randomly pick one of the security questions and then pass it to the form with initial={"hidden_answer": sec_qa.answer,}. However it doesn't seem the be going through, as I'm getting following error:
'NoneType' object has no attribute 'lower'
Exception Location: path/to/profiles/forms.py in clean_answer, line 103
formated_hidden_answer = self.cleaned_data.get("hidden_answer").lower()
OK, so NoneType would mean there's nothing to reference to. I've tried couple different ways to fix this. I tried putting hidden_answer in form's meta field list. I also tried {{ form.hidden_answer.as_hidden }} in the template (which is complete opposite of what I'm trying to achieve here, as the answer is still displayed in value of that hidden input in the page source). Any idea what am I doing wrong with this?
EDIT: If there's an alternative or a simple solution to what I'm trying to do, could you please reference any documentation about it?
Sending a hidden input can't prevent a user from knowing the hidden_answer. It won't be visible in browser but will very well be present in your DOM and accessible to any user. Sending the answer(hidden or not) to the client side is itself a flaw in security.
You should only send the question to the client side(browser) and later verify it in you clean() method.
If I understand you use case correctly(correct me if I'm wrong), you should do something like:
In your views.py, do something like:
def custom_register(request):
if request.user.is_authenticated():
return redirect(reverse('profile', host='profiles'))
if request.method == 'GET':
# Grab a random registration security Q&A
qa_count = SecurityQuestion.objects.count() - 1
sec_qa = SecurityQuestion.objects.all()[randint(0, qa_count)]
#Give the text of your question to sec_qa_title. Do something like the following.
#sec_qa_title = sec_qa.title
#sec_qa_title should now have the question string of the SecurityQuestion model object instance.
form = UserRegistrationForm(initial={'question' : sec_qa_title})
#initialize whatever context variables you want.
#Rest of your code.
#return a suitable response which will display you form with the security question.
#return render(request, 'profiles/register.html', context)
if request.method == 'POST':
#All the data of form submitted by the user is inside request.POST
form = UserRegistrationForm(request.POST)
# Validate the registration form
if form.is_valid():
#Do your stuff. Return a suitable response.
else:
#Do your stuff. Return a suitable response.
Now in your forms.py, do something like:
class UserRegistrationFirstForm(forms.ModelForm):
username = forms.CharField(max_length=20)
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
email = forms.EmailField()
email_confirm = forms.EmailField()
question = forms.CharField(max_length=50, required=True)
#removed hidden_answer field and added a question field.
answer = forms.CharField(max_length=50, required=True)
class Meta:
model = User
fields = [
'username',
'email',
'email_confirm',
'password',
'password_confirm',
#Remove the answer field.
]
def clean_answer(self):
security_question_title = self.cleaned_data.get("question")
#get the question title.
formatted_user_answer = self.cleaned_data.get("answer").lower()
#now get the SecurityQuestion model.
try:
sec_qa = SecurityQuestion.objects.get(title = security_question_title)
#Don't forget to import SecurityQuestion model.
except SecurityQuestion.DoesNotExist:
#If a user changes the question, you don't want it to fiddle with you system.
raise forms.ValidationError("Question was changed. Wrong practice.")
#Finally check the answer.
if formatted_user_answer != sec_qa.answer.lower():
raise forms.ValidationError("Incorrect answer to security question!")
return answer
There are many improvements that you can later try.
For example: Sending a question and an id with it to later extract the question through that id(instead of extracting it from the whole string; slightly unreliable)
I hope you understand the flow and construct it correctly.
There may be some errors since I didn't test the code but I hope you'll fix them.
I hope this guides you in some way. Thanks.

DRF auth_token: "non_field_errors": [ "Unable to log in with provided credentials."

Both JWT packages written for Django gave me issues with poor documentation, so I try DRF-auth_token package. This is a good example I followed, Django Rest Framework Token Authentication. You should in theory be able to go to
localhost:8000/api-token-auth/
urls.py:
from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.auth.models import User
from rest_framework.authtoken import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include('api.urls', namespace='api')),
url(r'^orders/', include('orders.urls', namespace='orders')),
url(r'^api-token-auth/', views.obtain_auth_token, name='auth-token'),
]
Getting a token for users is not working so I have rewritten it myself to make it work:
#api_view(['POST'])
def customer_login(request):
"""
Try to login a customer (food orderer)
"""
data = request.data
try:
username = data['username']
password = data['password']
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.get(username=username, password=password)
except:
return Response(status=status.HTTP_401_UNAUTHORIZED)
try:
user_token = user.auth_token.key
except:
user_token = Token.objects.create(user=user)
data = {'token': user_token}
return Response(data=data, status=status.HTTP_200_OK)
My version works:
http://localhost:8000/api/login/customer-login/
{"username": "thisguy#example.com", "password": "wombat"}
-->
{
"token": "292192b101153b7ced74dd52deb6b3df22ef2c74"
}
The DRF auth_token does not work:
http://localhost:8000/api-token-auth/
{"username": "thisguy#example.com", "password": "wombat"}
-->
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# third party:
'django_extensions',
'rest_framework',
'rest_framework.authtoken',
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
It seems set up correctly. Every user in my DB has a token. Each user is is_authenticated and is_active in DB. Super users can get their token:
localhost:8000/api-token-auth/
{"username": "mysuperuser", "password": "superuserpassword"}
-->
{
"token": "9297ff1f44dbc6caea67bea534f6f7590d2161b0"
}
for some reason, only super user can get a token:
localhost:8000/api-token-auth/
{"username": "regularguy", "password": "password"}
-->
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
Why can't my users log in and get their token? Thank you
I had the same error message when I tried to use this API endpoint :
" obtain_auth_token" class from rest_framework.authtoken.views , BUT surprise ! The problem was the User serializer in
first place ! .
Users were created with the API endppint ,their passwords were saved as plain text !, as in this screenshot :
User Database
BUT the TokenAPI endpoint encrypts the password, so from there come the clash! ,
I've changed User Serializer class and override create function to use set_password function that hashes the passwords :
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
Now that I've edited my User Serializer , data are stored like this :
User database after modification
And So the error : “non_field_errors”: [ “Unable to log in with provided credentials.” stopped showing ! , and the token API endpoint "localhost:8000/api-token-auth/" worked !
I went ahead and did this from the drf token auth docs and didn't run into any problems with superusers, staffusers, or normal users.
Also try following the steps of the official docs instead of that SO answer and see if that fixes the problem - it's possible something changed.
Here were the general steps I took:
install django, drf
put 'rest_framework' and 'rest_framework.authtoken' in INSTALLED_APPS
add 'TokenAuthentication' in my rest_framework settings
run migrate
create tokens for users (I just did this in urls.py)
create the url for token
POST http://localhost:8000/token/ {"username": "...", "password": "..."}
If you have the code public anywhere I'd be glad to take a further look and see what I find.
Maybe the encryption is to blame. I am facing the same problem. I compared the information stored in mysql of superuser and a common user (let's call it user1). I found a difference. The password of superuser was encrypted, but user1's password wasn't encryted. So I changed user1's password into the superuser's password, then I posted the name and password of user1 to the jwt api and I got the right answer.
And now I find an answer, though it may not be the best one, it should work.
I just overwrited the "create" method in "ModelSerializer".
step1: copy the "create" method from "ModelSerializer" to your own serializers file
step2: change the sentence "instance = ModelClass._default_manager.create(**validated_data)" into "instance = ModelClass._default_manager.create_user(**validated_data)".
step3: It worked]4
[]5
check username and password
field in the table users.is_active = 1
I hope this helps for those using TokenAuthentication (not JWT), django-allauth and dj-rest-auth and getting the same error.
This answer from another similar question worked for me.
I just needed to add these Authentication Backends on settings.py:
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend"
)
For me, I created the user with password 1234.
In the user admin panel I saw the below message
Password: Invalid password format or unknown hashing algorithm.
After updating the password with django password restrictions (minimum 8 characters and some others) I got the Token in resposne.
I got the same issue, I'm using Django 4.1. How I resolved:
If you're creating a superuser provide a strong password. Sometimes it does not provide the error on a weak password which happened to me and then won't generate the token.
If you are creating a user through code make sure to store the password as a hash.
Add the following class in setting.py installed_apps=[ 'rest_framework.authtoken',]
Now, give it a try!
the pasword is not correct
>>> nameko.Platform.Auth({'username': 'user', 'password': 'pass'})
[{'token': 'eefd5c0f747e121b9cb9986290f66b3c1089669d'}, 2
There can be several causes but IMHO the easiest way to clarify this, is to activate the logs in the settings file (with "level": "DEBUG") and to look at the generated SQL selection query by "api-token-auth"
for instance my personal jumped out in reading this request :
SELECT
profileapp_customuser.id, profileapp_customuser.password,
profileapp_customuser.last_login, profileapp_customuser.is_superuser,
[...]
profileapp_customuser.email FROM profileapp_customuser
WHERE
**profileapp_customuser.email = 'username_test3**' LIMIT 21;
In fact my custom model was inoperate cause my user unique id was no more username but email.
In my cases, I was using username and password for authentication. But the django authenticte method by was expecting email value against username key becuase of the following customized code in the project as it was already existing developed by someone else
class User(AbstractUser):
.....
USERNAME_FIELD = 'email'
So I provided that against username to make it functional.
See the screenshots for the reference
Using Username value
Using Email value
Note: it's because Django filter the username value against the field lookup as per mentioned USERNAME_FIELD value see the reference of the code below
https://github.com/django/django/blob/61d5e57353bb811df7b5457a1856baee31299429/django/contrib/auth/backends.py#L42
user = UserModel._default_manager.get_by_natural_key(username)
The solution to your problem is to back up authentication.
To login with rest_framework.authtoken, the login fields are username and password.
But in django jwt changed this field to USERNAME_FIELD.
You need to write a new Auth Backend and replace username with USERNAME_FIELD in the authentication function parameter.

Using email as username with django-registration

I am using django-registration 2.0.4 (https://django-registration.readthedocs.org/en/2.0.4/, https://github.com/ubernostrum/django-registration) with Django 1.8 and am trying to customise the registration form and application itself. While I am able to customise the form, I am stuck at using an email as username.
I don't need to change anything in Django's backend since simple removing the username field in the form and using the input email as username in the backend would be enough.
I have found this (Django-Registration: Email as username) for example but it is a couple of years old and I didn't manage to get it running yet.
How would I approach this the right way to implement said functionality? Has anyone solved this problem with a recent version already?
Edit:
Since I was asked for code: There isn't much that is relevant to this problem since it isn't a code problem but a question regarding how to extend or change the django plugin.
However, my current, boring, forms.py, as it is in the link I linked:
class MyCustomForm(forms.Form):
email = Email(required=True)
password1 = forms.CharField(widget=forms.PasswordInput(), label="Password")
password2 = forms.CharField(widget=forms.PasswordInput(), label="Repeat your password")
def clean_password(self):
if self.data['password1'] != self.data['password2']:
raise forms.ValidationError('Passwords are not the same')
return self.data['password1']
urls.py
...
url(r'^accounts/register/$', RegistrationView.as_view(form_class=InsuranceForm), name='registration_register'),
url(r'^accounts/', include('registration.backends.hmac.urls')),
...
I have done this using custom user models.
https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#auth-custom-user
There is a full example in the docs above that you will have to slightly modify to achieve your aims.

How do i create a custom django backend for django-registration?

I've had a read of this
http://docs.b-list.org/django-registration/0.8/backend-api.html
and i've had a shot at making my own backend. I am doing this because I want to create a backend that disallows having the same email used for registration, and I wanted to change the email-error message. I also wanted to add in my own field!
Here is what I came up with:
from django import forms
from registration.forms import RegistrationForm
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from registration.forms import attrs_dict
class customRegistrationForm(RegistrationForm):
email2 = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75)),
label=_("Confirm email"))
def clean_email(self):
"""
Validate that the email is alphanumeric and is not already
in use.
"""
try:
email = User.objects.get(email__iexact=self.cleaned_data['email'])
except User.DoesNotExist:
return self.cleaned_data['email']
raise forms.ValidationError(_("That email already exists - if you have forgotten your password, go to the login screen and then select \"forgot password\""))
def clean(self):
"""
Verifiy that the values entered into the two email fields
match. Note that an error here will end up in
``non_field_errors()`` because it doesn't apply to a single
field.
"""
if 'email' in self.cleaned_data and 'email2' in self.cleaned_data:
if self.cleaned_data['email'] != self.cleaned_data['email2']:
raise forms.ValidationError(_("The two email fields didn't match."))
return super(RegistrationForm,clean)
The above goes in my init.py file (whetever that is)
Then, I have in my urls.py code:
url(r'^accounts/register/$',
register,
{ 'backend': 'myapp.forms.customRegistrationForm' },
name='registration_register'),
... #other urls here!
Now, when i go to the /accounts/register page, I get the following error:
AttributeError at /accounts/register/
'customRegistrationForm' object has no attribute 'registration_allowed'
Which is weird. It seems to be telling me I need a "registration_allowed" method added to my subclass. BUT, the subclass is a subclass of the RegistrationForm, which works fine and doesn't have that stuff defined... I know I can add these members in, but it seems to beat the purpose of extension, right?
UPDATE
Here is the code now that it works!
I broke up the varying classes into varying init.py files in different folders - one called "forms" and one called "backends", both of which sit in a folder "djangoRegistration" under my main project.
/forms/init.py
from django import forms
from registration.forms import RegistrationForm
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from registration.forms import attrs_dict
class customRegistrationForm(RegistrationForm):
def __init__(self, *args, **kw):
super(RegistrationForm, self).__init__(*args, **kw)
self.fields.keyOrder = [
'username',
'email',
'email2',
'password1',
'password2'
]
email2 = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75)),
label=_("Confirm email"))
def clean_email(self):
"""
Validate that the email is alphanumeric and is not already
in use.
"""
try:
email = User.objects.get(email__iexact=self.cleaned_data['email'])
except User.DoesNotExist:
return self.cleaned_data['email']
raise forms.ValidationError(_("That email already exists - if you have forgotten your password, go to the login screen and then select \"forgot password\""))
def clean(self):
"""
Verifiy that the values entered into the two email fields
match. Note that an error here will end up in
``non_field_errors()`` because it doesn't apply to a single
field.
"""
if 'email' in self.cleaned_data and 'email2' in self.cleaned_data:
if self.cleaned_data['email'] != self.cleaned_data['email2']:
raise forms.ValidationError(_("The two email fields didn't match."))
return super(RegistrationForm,clean)
/backends/init.py
from registration.backends.default import DefaultBackend
from dumpstownapp.djangoRegistration.forms import customRegistrationForm
class customDefaultBackend(DefaultBackend):
def get_form_class(self, request):
"""
Return the default form class used for user registration.
"""
return customRegistrationForm
and finally, my urls.py just references the new backend:
url(r'^accounts/register/$',
register,
{ 'backend': 'myapp.djangoRegistration.backends.customDefaultBackend' },
name='registration_register'),
#more urls here! yay!
As a final note, I had to add some code to "order" the way the fields were presented, which is what the init method in the customRegistrationForm is doing
thanks!
You're trying to use a form as a backend, but that's not what a backend is at all. As the document you link to explains, a backend is a class that implements certain methods, including registration_allowed. The form doesn't implement any of those, which is not surprising, because it's meant for user input and validation, not the backend actions.
However, that page does give a hint as to the correct way to implement this. One of the methods that the backend can define is get_form_class(), which returns the form class to use. So, it seems that what you need is a custom backend that inherits from registration.backends.default.DefaultBackend and overrides just the get_form_class method, which simply returns customRegistrationForm.

Categories

Resources