How do I Send Request to EndPoint on application Startup - python

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.

Related

Django DRF how to validate captcha response for server side

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.

Django - How to add access token to Client.post in Django test?

So I have some code below. Every endpoint has an authentication process, which is below as well. I want to be able to attach an access token, which is in cls.user to the Client.post so that I can test all the endpoints and ensure they are authenticating properly as well. How can I do this? So ideally I'd be attaching <bearer> <access token> to request.Meta['HTTP_AUTHORIZATION']
test.py
import json
from cheers.models import *
from warrant import Cognito
from django.urls import reverse
from django.test import TestCase
from rest_framework import status
from cheers.models import GoalCategory, Post
from dummy_factory.Factories import UserFactory, GoalFactory
class PostTest(TestCase):
#classmethod
# Generates Test DB data to persist throughout all tests
def setUpTestData(cls) -> None:
cls.goal_category = 'health'
GoalCategory.objects.create(category=cls.goal_category, emoji_url='url')
cls.user = UserFactory()
cls.goal = GoalFactory()
user_obj = User.objects.get(pk=cls.user.phone_number)
goal_obj = Goal.objects.get(pk=cls.goal.uuid)
Post.objects.create(creator_id=user_obj, goal_id=goal_obj, body='Some text')
cls.user = Cognito(<Some login credentials>)
cls.user.authenticate(password=<password>)
def test_create(self):
response = self.client.post(reverse('post'),
data=json.dumps({'creator_id': str(self.user.uuid),
'goal_id': str(self.goal.uuid),
'body': 'Some text #Test'}),
content_type='application/json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
Test authenticator function
def cognito_authenticator(view_func):
def wrapped_view(request, *args, **kwargs):
# Check the cognito token from the request.
token = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
try:
jwt.decode_cognito_jwt(token)
except Exception:
# Fail if invalid
return Response("Invalid JWT", status=status.HTTP_401_UNAUTHORIZED) # Or HttpResponseForbidden()
else:
# Proceed with the view if valid
return view_func(request, *args, **kwargs)
return wrapped_view
You can set the header like this:
token = 'sometoken'
response = self.client.post(
reverse('post'),
data=json.dumps({
'creator_id': str(self.user.uuid),
'goal_id': str(self.goal.uuid),
'body': 'Some text #Test'
}),
content_type='application/json',
**{'HTTP_AUTHORIZATION': f'Bearer {token}'}
)
And then access the header using:
request.META['HTTP_AUTHORIZATION']

implementing flask_jwt_extended with templates rendering

Again fighting trying to make my first flask application, this time, (after I created every I need and all works smoothly) I'm trying to protect some endpoints with flask_jwt_extended, but I can't find how to work with them in my pages, the documentation is mostly about displaying JSON messages and some tutorials use postman while in my case I'm using HTML templates.
For example, a user sends his credentials from the login page to this endpoint :
#app.route('/login', methods=['POST'])
def UserLogin():
data = parser.parse_args()
current_user = UserModel.find_by_username(data['username'])
if not current_user:
return {'message': 'User {} doesn\'t exist'.format(data['username'])}
if UserModel.verify_hash(data['password'], current_user.password):
access_token = create_access_token(identity = data['username'])
refresh_token = create_refresh_token(identity = data['username'])
resp = jsonify({'login': True}) #I just added this line from the documentation
set_access_cookies(resp, access_token) # and this one
set_refresh_cookies(resp, refresh_token) # and this one
return redirect(url_for('results'))
else:
return {'message': 'Wrong credentials'}
and of course, I added the #jwt_required decorator the results endpoint:
#app.route('/result',methods = ['POST','GET'])
#jwt_required
def results():
temp={}
if request.method == 'POST':
# some code to fill temp with values
return render_template('result.html',data=temp)
So I'm getting a {
"msg": "Missing cookie \"access_token_cookie\""
}Obviously because I'm not sending the jwt back but if send it in the return statement how can I redirect the user the page I want ?? And indeed I used app.config['JWT_TOKEN_LOCATION'] = ['cookies']
You may want to:
resp = make_response(redirect(url_for('results')))
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
I don't think you need this line! --> resp = jsonify({'login': True})
Took me a while to figure it out, not sure why this part is not clear in the docs, most of the examples there just returns JSON directly
Also, you get same error if JWT_ACCESS_COOKIE_PATH is routed wrongly.

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

How to test an API endpoint with Django-rest-framework using Django-oauth-toolkit for authentication

I have a Django-rest-framework viewset/router to define an API endpoint. The viewset is defined as such:
class DocumentViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
model = Document
And the router is defined as
router = DefaultRouter()
router.register(r'documents', viewsets.DocumentViewSet)
with url pattern url(r'^api/', include(router.urls))
I can hit this endpoint in the browser/through curl just fine by getting the right access token and using it for authorization. However, it's not clear how to write tests against this endpoint.
Here is what I've tried:
class DocumentAPITests(APITestCase):
def test_get_all_documents(self):
user = User.objects.create_user('test', 'test#test.com', 'test')
client = APIClient()
client.credentials(username="test", password="test")
response = client.get("/api/documents/")
self.assertEqual(response.status_code, 200)
This fails with an HTTP 401 response from the client.get() call. What is the right way to test an API endpoint in DRF using django-oauth-toolkit for oauth2 authentication?
When you are writing tests, you should aim to extract anything you are not testing from the test itself, typically putting any setup code in the setUp method of the test. In the case of API tests with OAuth, this usually includes the test user, OAuth application, and the active access token.
For django-oauth-toolkit, and other Django applications, I would always recommend looking at the tests to see how they do it. This allows you to avoid making unneeded API calls, especially for multi-part processes like OAuth, and only create the few model objects that are required.
def setUp(self):
self.test_user = UserModel.objects.create_user("test_user", "test#user.com", "123456")
self.application = Application(
name="Test Application",
redirect_uris="http://localhost",
user=self.test_user,
client_type=Application.CLIENT_CONFIDENTIAL,
authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE,
)
self.application.save()
def test_revoke_access_token(self):
from datetime import datetime
from django.utils import timezone
tok = AccessToken.objects.create(
user=self.test_user, token='1234567890',
application=self.application, scope='read write',
expires=timezone.now() + datetime.timedelta(days=1)
)
From there you just need to authenticate using the token that has been generated. You can do this by injecting the Authorization header, or you can use the force_authenticate method provided by Django REST Framework.
I have used the same library for OAuth2,
This worked for me
from oauth2_provider.settings import oauth2_settings
from oauth2_provider.models import get_access_token_model,
get_application_model
from django.contrib.auth import get_user_model
from django.utils import timezone
from rest_framework.test import APITestCase
Application = get_application_model()
AccessToken = get_access_token_model()
UserModel = get_user_model()
class Test_mytest(APITestCase):
def setUp(self):
oauth2_settings._SCOPES = ["read", "write", "scope1", "scope2", "resource1"]
self.test_user = UserModel.objects.create_user("test_user", "test#example.com", "123456")
self.application = Application.objects.create(
name="Test Application",
redirect_uris="http://localhost http://example.com http://example.org",
user=self.test_user,
client_type=Application.CLIENT_CONFIDENTIAL,
authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE,
)
self.access_token = AccessToken.objects.create(
user=self.test_user,
scope="read write",
expires=timezone.now() + timezone.timedelta(seconds=300),
token="secret-access-token-key",
application=self.application
)
# read or write as per your choice
self.access_token.scope = "read"
self.access_token.save()
# correct token and correct scope
self.auth = "Bearer {0}".format(self.access_token.token)
def test_success_response(self):
url = reverse('my_url',)
# Obtaining the POST response for the input data
response = self.client.get(url, HTTP_AUTHORIZATION=self.auth)
# checking wether the response is success
self.assertEqual(response.status_code, status.HTTP_200_OK)
Now everything will work as expected.
Hope this helps. Thanks
from oauth2_provider.models import (
get_access_token_model,
get_application_model,
get_id_token_model,
get_refresh_token_model,
)
class TestOauth(APITestCase):
def setUp(self):
""" create and register user """
self.test_user = User.create..
def test_oauth_application_and_tokens_add(self):
print(self.test_user, self.test_user.id)
"""Applications"""
Application = get_application_model()
app = Application()
app.name = "test"
app.client_type = "confidential"
app.authorization_grant_type = "password"
app.user_id = self.test_user.id
app.save()
# client_id:
print("Application Client ID: ", app.client_id)
# client_secret:
print("Application Client SECRET: ", app.client_secret)
"""Access Token"""
AccessToken = get_access_token_model()
token = AccessToken()
token.user_id = self.test_user.id
token.scope = "read write"
token.expires = timezone.now() + timezone.timedelta(seconds=300)
token.token = "secret-access-token-key"
token.application = app
token.save()
# token
print("Access Token: ", token)
self.auth = "Bearer {0}".format(token.token)
""" ID Token """
IDToken = get_id_token_model()
idt = IDToken()
idt.user_id = self.test_user.id
idt.application = app
idt.expires = timezone.now() + timezone.timedelta(days=10)
idt.scope = "read write"
idt.save()
# id token - returns jti token - successfull
print("ID Token: ", idt)
""" Refresh Token """
RefreshToken = get_refresh_token_model()
refr = RefreshToken()
refr.user_id = self.test_user.id
refr.application = app
refr.token = "statictoken" # The token is issued statically.
refr.access_token = (
token # The access token must not have been used before.
)
refr.revoked = timezone.now() + timezone.timedelta(days=10)
refr.save()
# refresh token
print("Refresh Token: ", refr)

Categories

Resources