Django DRF how to validate captcha response for server side - python

I amusing nextjs as my frontend. I am struggling to validate server site captcha response. I want data will be not inserted in my database until captcha verification sucess. I tried drf-recaptcha package. Here is my code:
settings.py
INSTALLED_APPS = ['rest_framework_recaptcha',] #included in my installed app
DRF_RECAPTCHA_SECRET_KEY = '< my_secrect key>'
serializers.py
from rest_framework_recaptcha import ReCaptchaField
class ContactSerializer(serializers.ModelSerializer):
recaptcha = ReCaptchaField()
class Meta:
model = Contact
fields = '__all__'
my API views.py
#api_view(['POST', 'GET'])
def contact_forms_api(request):
if request.method == 'POST':
''' Begin reCAPTCHA validation '''
recaptcha_response = request.POST.get('g-recaptcha-response')
url = 'https://www.google.com/recaptcha/api/siteverify'
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': recaptcha_response
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
result = json.load(response)
''' End reCAPTCHA validation '''
if result['success']:
data = request.data
serializer = ContactSerializer(data=data)
if serializer.is_valid():
serializer.save()
print(serializer.data)
return Response({
'status': True,
'message': 'sucess'
})
return Response({
'status': False,
'message': serializer.errors
})
here is my nexjs code:
I am using this package in my frontend application. here my code sometings look like:
<form onSubmit={SubmitContact}>
<input type="text"/>
<ReCAPTCHA
sitekey="my_captcha site ke=y"
onChange={handleRecaptcha}
/>
<form>
here is my SubmitContact function look like this:
const res = await axios.post(url,data,{headers: headers}
)
inside data variable I am passing user data and passing this headers = { 'Content-Type': 'application/json', } as header.
Conclution: I know this question is very long but I think I need to explain everything so you can understand my problems.
Right now anyone can submit data using my contact froms but I want they must be verify captcha and the captcha response must be verified from serverside.

Related

CSRF verification failed. Request aborted in Django rest framework sending the request from flutter

I've followed everything mentioned in both documentation of Django rest-framework and Flutter http but still getting the error ..here is my code :
Django
Settings
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}
View
#csrf_exempt
#permission_classes(["isAuthenticated"])
#api_view(['POST'])
def chanage_image(request):
data = {}
if request.method == "POST":
token = request.META['HTTP_AUTHORIZATION'][6:]
lang = request.META['HTTP_LANG']
image = request.data['image']
main_user = Token.objects.get(key=token).user
app_user = AppUser.objects.get(main_user=main_user)
format, imgstr = image.split(';base64,')
ext = format.split('/')[-1]
data = ContentFile(base64.b64decode(imgstr), name='temp.' + ext) # You can save this as file instance.
app_user.image = data
app_user.save()
data = {"success": True, "details": AppUserSerializer(
app_user).data, "message": "Image changed" if lang == "en" else "تم تغيير الصورة"}
return Response(data, headers=get_headers())
URLS
path('chanage_image/', chanage_image,name="chanage_image"),
Flutter
Request
Map<String, dynamic> body = {
"image": base64Image,
};
Future<UserModel> changePlayerImage(Map<String, dynamic> body) async {
return await httpClient.post('api/user/change-image',
body: body,
headers: {'referer': 'https://www.l-dawri.com/'}).then((response) {
print(response.body);
return UserModel.fromJson(response.body);
});
}
but still in the end am always getting this error :
CSRF verification failed. Request aborted.
You are seeing this message because this site requires a CSRF cookie when submitting forms.
First you don't sent authorization token into header request while use from drf TokenAuthentication
Also into drf is better you use from class view api(like inheritance from APIView) replace def view's

How do I Send Request to EndPoint on application Startup

I am working on a Django project which has an API endpoint that receives a post request and sends a welcome email to the registered user, currently, I have to use a form to send this request to the endpoint, is there a way to manually read the email and name from my environmental variable and send a request once I run the app the first time? something like
EMAIL = 'try#test.com'
NAME = 'Bob'
I have this stored as an env variable already
and here is my current code
#require_http_methods(["POST"])
#login_required
def add_user(request):
if request.is_ajax():
name = request.POST.get('name')
email = request.POST.get('email')
if not BlueUsers.objects.filter(user_email=email).exists():
newuser_obj = BlueUsers.objects.create(user_name=name, user_email=email)
conf_obj = Config.objects.first()
if conf_obj:
post_url = "{}/priv/create-user/".format(conf_obj.hostname)
data = {
'name': newuser_obj.user_name,
'email': newuser_obj.user_email,
'redtree_user_id': newuser_obj.id
}
headers = {'data-auth-key': conf_obj.authentication_token}
try:
response = requests.post(post_url, data=data, headers=headers)
except:
response = None
I have been struggling with this
There is a file name apps.py that loads your app configuration and runs code on app startup.
Your purpose should be served by following piece of code
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
# your model and other imports here
email = os.environ.get('EMAIL')
name = os.environ.get('NAME')
if not BlueUsers.objects.filter(user_email=email).exists():
newuser_obj = BlueUsers.objects.create(user_name=name, user_email=email)
conf_obj = Config.objects.first()
if conf_obj:
post_url = "{}/priv/create-user/".format(conf_obj.hostname)
data = {
'name': newuser_obj.user_name,
'email': newuser_obj.user_email,
'redtree_user_id': newuser_obj.id
}
headers = {'data-auth-key': conf_obj.authentication_token}
try:
response = requests.post(post_url, data=data, headers=headers)
except:
response = None
Any logic that you write inside the ready method of your AppConfig class is going to be executed once on each startup.

Django testing in creating a user with email address which is from session input, assertRedirects is not working

The user creation is using an email address as USERNAME_FIELD and it is extracted from session and save in the form save(). It seems it is not going further down to the redirection. How can I test the redirection in this case?
tests.py:
class RegistraionViewTest(TestCase):
valid_data = {
'email': 'good#day.com',
'password1': 'test1234',
}
kwargs = {
'email': 'good#day.com'
}
def test_registration(self):
response = self.client.post(reverse('registration'), data=self.valid_data, follow=True)
self.assertTrue(response.context['form'].is_valid())
# mocking the session input
response.context['form'].save(email=self.kwargs['email'])
self.assertTrue(account.check_password(self.valid_data['password1']))
# working so far, but it seems there is no redirect url in response
self.assertRedirects(response, reverse('next_url'))
In views.py:
if request.method == 'POST':
form = RegistraionForm(request.POST)
if form.is_valid():
email = request.session.get('email')
try:
account = form.save(email=email)
return HttpResponseRedirect('next_url'))
In forms.py:
def save(self, **kwargs):
user = super(RegistrationForm, self).save(commit=False)
user.email = kwargs.pop('email')
user.save()
return user
It seems there is no url in the response in tests.py. What went wrong here?
Your response may be a 500, not a 302, which would mean there is no Location header.
The call for request.session.get('email') will likely throw a KeyError, as your test does not appear to set the session['email'] field, and there is no default.
Note that when using a session in a test case, you need to assign it to a variable in the beginning, as in the example below (from Django Testing Tool docs):
def test_registration(self):
session = self.client.session
session['email'] = self.kwargs['email']
session.save()
# and now make your call to self.client.post
response = self.client.post(...)
self.assertEqual(response.status_code,302)
# .. and the rest

Multiple error messages trying to send file to server using Requests and Django

I'm currently using the Requests library to send a file to a remote server from a form(InMemoryUploadedFile). I initially sent the 'file'(file = self.request.FILES.get('file')) as part of my payload, and when I ran the code I received a JSON error response from the server that says:
{"outcome":"error","message":"string contains null byte"}
Upon further reading(https://docs.djangoproject.com/en/1.9/ref/files/uploads/) it seems like it would make sense to read the file. So I decided to read the file using the .chunks() method(in case you have files larger than 2.5MB), but now I'm getting a:
{"outcome":"error","message":"invalid byte sequence in UTF-8"}
And if I use .multiple_chunks() I get a server 500 error.
Does anyone have any ideas what steps could be taken to resolve this issue?
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
login_url = reverse_lazy('users:login')
form_class = FileUploadForm
template_name = 'docman/forms/add-document.html'
success_message = 'Document was successfully added'
def form_valid(self, form):
pk = self.kwargs['pk']
user = get_object_or_404(User, pk=pk)
file = self.request.FILES.get('file')
if not self.post_to_server(file, user.id):
messages.error(self.request, "Upload failed", extra_tags='alert alert-danger')
return render(self.request, self.template_name, {'form': form})
def post_to_server(self, file, cid):
url = 'https://exampleapi.herokuapp.com/api/files/'
headers = {'token': 'secret-token93409023'}
payload = {'file': file.chunks(), 'client_id': cid}
r = requests.post(url, data=payload, headers=headers)
print(r.text)
if r.status_code == requests.codes.ok:
return True
else:
return False
I figured it out, it had to do with me sending the file as part of the payload, which doesn't multipart encode the upload. So what I had to do was send the file as the correct 'file' parameter which does properly multipart encode the upload.
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
login_url = reverse_lazy('users:login')
form_class = FileUploadForm
template_name = 'docman/forms/add-document.html'
success_message = 'Document was successfully added'
def form_valid(self, form):
pk = self.kwargs['pk']
user = get_object_or_404(User, pk=pk)
file = self.request.FILES.get('file')
if not self.post_to_server(file, user.id):
messages.error(self.request, "Upload failed", extra_tags='alert alert-danger')
return render(self.request, self.template_name, {'form': form})
def post_to_server(self, file, cid):
url = 'https://exampleapi.herokuapp.com/api/files/'
headers = {'token': 'secret-token93409023'}
# Remove files as part of payload
payload = {'client_id': cid}
files = {'file': file}
# Place 'files' as file paramter to be properly multipart encoded
r = requests.post(url, files=files, data=payload, headers=headers)
print(r.text)
if r.status_code == requests.codes.ok:
return True
else:
return False

CSRF verification fails on requests.post()

Can't figure this one out. The CSRF verification works fine in all my Django template views. Here I'm trying to post from a python client using techniques I've found in other posts. The client.get(url) call does provide the token (the debugger shows, for example: client.cookies['csrftoken'] = 'POqMV69MUPzey0nyLmifBglFDfBGDuo9') but requests.post() fails with error 403, CSRF verification failed. What's going on?
My Django view (with some dummy stuff in the methods):
class CameraUpload(View):
template_name = "account/templates/upload.html"
def get(self, request):
dummy = VideoForm()
return render(request, self.template_name, {'form': dummy})
def post(self, request):
dummy = VideoForm()
return render(request, self.template_name, {'form': dummy})
And the client that's trying to do the post:
import requests
url = 'http://127.0.0.1:8000/account/camera_upload/'
client = requests.session()
client.get(url)
csrftoken = client.cookies['csrftoken']
payload = {
'csrfmiddlewaretoken': csrftoken,
'tag': '69'
}
r = requests.post(url, data=payload)
EDIT:
Tried adding the referer as per this link so code now looks like:
r = requests.post(url, data=payload, headers=dict(Referer=url))
but same problem exists.
You should be using your session (client) for the post:
r = client.post(url, data=payload, headers=dict(Referer=url))

Categories

Resources