I made an endpoint called /cars.
A person can create cars with a frontend, but devices read cars using an SDK, which has an API Key. This way, 2 rent-a-car companies can use the API without getting the cars mixed-up. Each app has its own API Key and its own person managing the contents.
This is being implemented with django restframework 3.x and django-oauth-toolkit.
I'm writing a test for a human retrieving cars, and another for a device.
This is failing:
def test_get_list(self):
# devices have a django user (AUTH_USER_MODEL ---onetoone--- Device)
self.client.force_authenticate(user=self.user_device)
self._get_list()
self.client.force_authenticate(user=None)
force_authentication sets request.auth to None. However, with postman or httpie, request.auth contains the Application object.
The queryset is:
def get_queryset(self):
if hasattr(self.request.user, 'device'):
# get the cars created by the owner of the API Key
return self.request.auth.application.user.cars.all()
return self.request.user.cars.all() # get my cars
Does this approach in the queryset make sense?
Am I testing it in the wrong way?
Why is request.auth empty? Is force_authentication using BasicAuthentication?
I would recommend going with check_object_permission for this kind of checks. You can read more here.
DRF documentation states that you need to force_authenticate the request if you are using APIRequestFactory. From the documentation:
from rest_framework.test import force_authenticate
factory = APIRequestFactory()
user = User.objects.get(username='olivia')
view = AccountDetail.as_view()
# Make an authenticated request to the view...
request = factory.get('/accounts/django-superstars/')
force_authenticate(request, user=user)
response = view(request)
To authenticate with APIClient try using credentials. Example from the documentation:
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient
# Include an appropriate `Authorization:` header on all requests.
token = Token.objects.get(user__username='lauren')
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
The same is the second question.
As pointed in the documentation force_authenticate bypass authentication therefore it's your job to simulate the missing authentication part, including filling the request.auth.
Otherwise, you'll need to configure a data set and call either login or credential on the APIClient instance.
Related
I have written a simple Python Flask API which does operations like adding data to Database and getting data from Database, there is no UI for this API, Now I want to implement OAuth authentication system for this simple API, As there is NO GUI, I cant use google or FB Oauth Providers which redirects users to there login page.
In simple words, i want to create my own GUI less oauth Authentication system which secures my API as any user who wants to access my API should pass through this authentication system by passing access token in a header
I need Oauth Authentication system of my own for the API's below:
from flask import Flask, redirect, url_for, session
from flask import Flask,jsonify,request,make_response
from flask_login import login_user,logout_user,current_user,login_required,LoginManager,login_manager
from flask_oauth import OAuth
import json
from flask_mysqldb import MySQL
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_oauthlib.provider import OAuth1Provider
app = Flask(__name__)
class MYWIFI(db.Model):
__tablename__ = 'MYWIFI'
id = db.Column('id', db.Integer, primary_key=True)
data = db.Column('data', db.Unicode)
def __init__(self, id, data):
self.id = id
self.data = data
#app.route('/getall')
def getall():
access_token = get_access_token()
if access_token is None:
return redirect(url_for('login'))
else:
languages = [u.__dict__ for u in db.session.query(MYWIFI).all()]
for d in languages:
del d['_sa_instance_state']
print(languages)
languagesJSON = json.dumps(languages)
return languagesJSON
#app.route('/insert', methods=['GET','POST'])
def insert():
access_token = get_access_token()
if access_token is None:
return redirect(url_for('login'))
else:
if request.method == 'POST':
insert = request.get_json()
id = insert['id']
data = insert['data']
print id
print data
new = MYWIFI(id, data)
db.session.add(new)
db.session.commit()
return "Success"
def main():
app.run()
if __name__ == '__main__':
main()
Please can anyone help me in kick starting this
I appreciate for this help
If I understood correctly, what you want is to build API endpoints which are protected by OAuth 2.0 tokens. If that's the case you as the API builder does not have to worry how token obtaining process happens. The client that consumes your APIs must perform the token obtaining and pass them to your end.
About sending in headers, try to stick with standards already exist in the OAuth 2 domain. RFC6750 defines how to use tokens once a client obtains them. It defines bearer authentication schema to transmit access tokens. Check section 2.1 to how header is set.
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
Where mF_9.B5f-4.1JqM is the access token. Once your API receives a request, from your end you must validate the access token before granting access. For this there is RFC7662 which define the methodology to validate access token against the authorization server. See section 2 Introspection endpoint to get an understanding of it. Alternatively, access token can come in a JWT format thus allowing it to be self contained.
Unfortunately, I do not have code for proposed solutions. They will considerable amount of codes. But I suggest you to separate authorization logic from your code. That means validation of authorization must be a separate module in your python code. But below I give a suggestion with my python knowledge.
#app.route('/insert', methods=['GET','POST'])
def insert():
access_token = get_access_token()
# Auth Validation for insert - This is based on access tokens
# If token is invalid/not-present, exception is thrown with HTTP 401 - unauthorized
my_auth_module.validate_access_token(access_token)
if request.method == 'POST':
insert = request.get_json()
id = insert['id']
Also one final thing, your API should not worry about redirection for login. Let it be handled by your API client upon the 401 - Unathorized response.
I am working in Django 1.8 with the excellent django-rest-framework. I have a public RESTful API.
I would now like to start requiring a key GET parameter with this API, and disallowing any requests that do not have this parameter. I will allocate keys to users manually on request.
I have read through the DRF Authentication documentation, but I'm not sure there's anything that meets my use case. I find this strange, since my use case must be very common.
Token-based authentication requires the user to set an HTTP header. My typical API user is not sophisticated (Excel users who will be downloading CSVs), so I don't think I can ask them to do this.
I think Basic-Auth is what I need, but I'd much rather provide a simple URL-based key than a Django username and password (my app has no concept of users right now).
What is the best way to implement this?
Create a table which will contain all the keys that you issue to someone.
Example:
class RestApiKey(models.Model):
api_key = models.CharField(max_length=100)
Next create a custom Permision class which will check for the api Key in the url before forwarding the request to the view like:
from rest_framework import permissions
from yourappname.models import RestApiKey
class OnlyAPIPermission(permissions.BasePermission):
def has_permission(self, request, view):
try:
api_key = request.QUERY_PARAMS.get('apikey', False)
RestApiKey.objects.get(api_key=api_key)
return True
except:
return False
So the request url has to be like http://yourdomain.com/?apikey=sgvwregwrgwg
Next in your views add the permission class:
class YourViewSet(generics.ListAPIView):
permission_classes = (OnlyAPIPermission,)
or if you are using function based views then do like:
#permission_classes((OnlyAPIPermission, ))
def example_view(request, format=None):
. . .
I am trying to decide whether I should use Django's Client or RequestFactory to test my views.
I am creating my server using DjangoRESTFramework and it's really simple, so far:
class SimpleModelList(generics.ListCreateAPIView):
"""
Retrieve list of all route_areas or create a new one.
"""
queryset = SimpleModel.objects.all()
serializer_class = SimpleModelSerializer
filter_backends = (IsOwnerFilterBackend,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
What are the differences between testing with Django's Client and RequestFactory and which approach is more suited for testing a REST server (if there is any difference besides liking one better)?
Should I create tests with both so as to provide a better coverage for my system?
RequestFactory and Client have some very different use-cases. To put it in a single sentence: RequestFactory returns a request, while Client returns a response.
The RequestFactory does what it says - it's a factory to create request objects. Nothing more, nothing less.
The Client is used to fake a complete request-response cycle. It will create a request object, which it then passes through a WSGI handler. This handler resolves the url, calls the appropriate middleware, and runs the view. It then returns the response object. It has the added benefit that it gathers a lot of extra data on the response object that is extremely useful for testing.
The RequestFactory doesn't actually touch any of your code, but the request object can be used to test parts of your code that require a valid request. The Client runs your views, so in order to test your views, you need to use the Client and inspect the response. Be sure to check out the documentation on the Client.
When using Django REST framework request factory would be helpfull to test the permissions.
EX:
Class TestPermission(TestCase):
def test_admin_permisiion(self):
admin_user = User.objects.create(email='admin#gmail.com',password='admin997',is_staff=True)
factory = RequestFactory()
request = factory.get('/')
request.user = admin_user
permission = IsAdminUser()
has_permission = permission.has_permission(request, None)
self.assertTrue(has_permission)
what we have done hear is we created a admin user by setting is_staff=True , then we created a request and assigned the admin as user of the request. request factory helps us do so. then we checked the IsAdminUser() permission from DRF against the request. the test will pass .
Client is to be used when you need to test the response returned by an Api.
I am relatively new with Django and it's ecosystem. I am writing REST api for our mobile client using django-tastypie. I have gone through almost all the examples on the web about how to use tastypie for creating REST interfaces. but none of them are specific to POSTing the data from client and how would you authorize a client.
I used the from tastypie.authentication.BasicAuthentication as show in the example. It opens a pop up asking username and password and works fine on the browser. But I am not sure, if it will do the same thing on mobile (to be specific, native IOS app). I am not quite getting when a user will make a request to login how this popup will be shown there on his/her mobile device if he or she is not using the browser but the native app.
I am totally lost on this, I would really appreciate your help.
You can check out source and use for example ApiKeyAuthentication.
You just have to POST username and api key to authentificate user.
It looks like usable for ios app.
Here is the part of the checking code.
def is_authenticated(self, request, **kwargs):
"""
Finds the user and checks their API key.
Should return either ``True`` if allowed, ``False`` if not or an
``HttpResponse`` if you need something custom.
"""
from django.contrib.auth.models import User
username = request.GET.get('username') or request.POST.get('username')
api_key = request.GET.get('api_key') or request.POST.get('api_key')
if not username or not api_key:
return self._unauthorized()
try:
user = User.objects.get(username=username)
except (User.DoesNotExist, User.MultipleObjectsReturned):
return self._unauthorized()
request.user = user
return self.get_key(user, api_key)
https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authentication.py#L128
https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authorization.py#L42
Thanks for the help.
I used similar approach mentioned by #Iurii. Here is my solution.
I wrote a class for handling the authentication and override is_authenticated method. and then I can use this class in Meta definition of tastypie resource classes.
from tastypie.authentication import BasicAuthentication
from tastypie.resources import Resource, ModelResource
# class for handling authentication
class MyAuthentication(BasicAuthentication):
def is_authenticated(self, request, **kwargs):
# put here the logic to check username and password from request object
# if the user is authenticated then return True otherwise return False
# tastypie resource class
class MyResource(ModelResource):
class Meta:
authentication = MyAuthentication()
this will ensure a request to access the resource will go through your authentication code.
I'm currently developing an application in Django and trying to implement Facebook authentication and requests to the Graph API. I've seen a few different libraries out there, but what is the best way to do the following:
Have a user login via Facebook.
Django creates a new user for them and adds their uid and oauth token.
I can then make calls to the Graph API using Facebook's Python SDK.
I did see this example. Is it that simple on normal Django?
My company has built a library that makes integrating Facebook into your Django application dead simple (we've probably built 10-20 apps with the library, including some with huge amounts of traffic, so it's been battle-tested).
pip install ecl-facebook==1.2.7
In your settings, add values for your FACEBOOK_KEY, FACEBOOK_SECRET, FACEBOOK_SCOPE, FACEBOOK_REDIRECT_URL, and PRIMARY_USER_MODEL. You'll also need to add ecl_facebook.backends.FacebookAuthBackend to your AUTHENTICATION_BACKENDS. For example, in settings.py:
# These aren't actual keys, you'll have to replace them with your own :)
FACEBOOK_KEY = "256064624431781"
FACEBOOK_SECRET = "4925935cb93e3446eff851ddaf5fad07"
FACEBOOK_REDIRECT_URL = "http://example.com/oauth/complete"
FACEBOOK_SCOPE = "email"
# The user model where the Facebook credentials will be stored
PRIMARY_USER_MODEL = "app.User"
AUTHENTICATION_BACKENDS = (
# ...
'ecl_facebook.backends.FacebookAuthBackend',
)
Add some views in your views.py to handle pre- and post-authentication logic.
from django.contrib.auth import authenticate, login
from django.http import HttpResponseRedirect
from ecl_facebook.django_decorators import facebook_begin, facebook_callback
from ecl_facebook import Facebook
from .models import User
# ...
#facebook_begin
def oauth_facebook_begin(request):
# Anything you want to do before sending the user off to Facebook
# for authorization can be done here.
pass
#facebook_callback
def oauth_facebook_complete(request, access_token, error):
if error is None:
facebook = Facebook(token)
fbuser = facebook.me()
user, _ = User.objects.get_or_create(facebook_id=fbuser.id, defaults={
'access_token': access_token})
user = authenticate(id=user.id)
login(request, user)
return HttpResponseRedirect("/")
else:
# Error is of type ecl_facebook.facebook.FacebookError. We pass
# the error back to the callback so that you can handle it
# however you want.
pass
Now just hook up these URLs in your urls.py file and you're done.
# ...
urlpatterns = patterns('app.views',
# ...
url(r'^oauth/facebook/begin$', 'oauth_facebook_begin'),
url(r'^oauth/facebook/complete$', 'oauth_facebook_complete'),
)
Hope this helps!
P.S. You can read the rest of the docs here.
We do a lot of Facebook Application development where I work, and so we've developed an open-source library that makes everything about it really easy.
from django.http import HttpResponse
from fandjango.decorators import facebook_authorization_required
#facebook_authorization_required
def foo(request, *args, **kwargs):
return HttpResponse("Your name is %s" % request.facebook_user.first_name)
I recommend https://github.com/egnity/fb.py. Got my Django-based Facebook app up and running in no time. It includes a middleware that allows you to run code like this in your view:
for the user id:
user_id = request.facebook.graph().get_object("me")['id']
for the oauth token:
user_token = request.facebook.auth_token
You can then add the above to your User model as you please. To make Graph API calls, you can still use fb.py's middleware -- no need for using the primitive python-sdk. The user_id code above is a perfect example of a Graph API call. There's much more you can do with fb.py. The download includes a sample django project to get you going.