I want to pass a value through the Headers of a get request.
Im trying the below but it doesn't work,
class ListCategoriesView(generics.ListAPIView):
"""
Provides a get method handler.
"""
serializer_class = CategorySerializer
def get(self, request, *args, **kwargs):
token = request.data.get("token", "")
if not token:
"""
do some action here
"""
if not UserAccess.objects.filter(accessToken=token).exists():
"""
do some action here
"""
else:
"""
do some action here
"""
I want to pass the token in the headers like that :
can anyone help me with this issue,
thanks a lot in advance.
You said it yourself, you're passing it in the headers, so you need to get it from there. DRF does not do anything special to access the headers, so it proxies to the underlying Django HttpRequest object, which makes them available via the META attribute, converted to uppercase and prefixed by HTTP_:
token = request.META.get("HTTP_TOKEN", "")
Related
I have created a callback view for instagram account connection in django by inheriting the APIView class.
After successful connection of instagram account facebook redirects me to the InstagramConnectCallbackView and includes the response data as a URL fragment.
url:
http://localhost:8000/v1/instagram-connect/callback/?#access_token=EAAN....&data_access_expiration_time=1650543346&expires_in=6254&state=eyd...
But I don't know how to read the URL fragments from the request into the get method.
callback view:
class InstagramConnectCallbackView(APIView):
permission_classes = (permissions.AllowAny,)
version = settings.FACEBOOK_GRAPH_API_VERSION
def get(self, request, format=None):
....
I tried the following:
request.get_full_path() # returns `/v1/instagram-connect/callback/`
request.query_params() # returns `{}`
Any help will be appreciated.
You can use query_params as
request.query_params.get('your_key_name')
If you want default then yot can use
self.request.query_params.get('your_key_name', None)
In your case you can get values like
access_token = self.request.query_params.get('access_token', None)
data_access_expiration_time = self.request.query_params.get('data_access_expiration_time', None)
expires_in = self.request.query_params.get('expires_in', None)
and so on ...
I've created a base api view, which extends from APIView, where I log response time, log request, and other common stuffs.
Now, I also want to add request validation here, using the Serializer defined in sub-class Views. I thought the appropriate place is to put that in dispatch() method. But before I call API.dispatch() method, request.data is not prepared. So, that won't work. Can someone help me in right direction as to how to move validation to a single place?
Here's the class structure:
class BaseView(APIView):
validation_serializer = None
def dispatch(self, request, *args, **kwargs):
# Some code here
# How to use `validation_serializer` here, to validate request data?
# `request.data` is not available here.
response = super(BaseView, self).dispatch(request, *args, **kwargs)
# Some code here
return response
class MyView(BaseView):
validation_serializer = ViewValidationSerializer
def post(self, request, *args, **kwargs):
pass
I thought another approach could be use decorator on the top of post() method. But if only there was an cleaner way, than putting decorators all across the project?
Note: It's similar to the question here: Django - DRF - dispatch method flow. But as per the suggestion there, I don't want to just copy the entire dispatch method from DRF source code.
The method that processes the django request into a DRF request (and adds the request.data property) is the APIView.initialize_request . The APIView.dispatch() method calls it and then proceeds to call the appropriate method handler (post/patch/put).
You can try to do that yourself by calling it and using the returned object:
class BaseView(APIView):
validation_serializer = None
def dispatch(self, request, *args, **kwargs):
request = self.initialize_request(request, *args, **kwargs)
kwargs['context'] = self.get_serializer_context()
serializer = self.validation_serializer(data=request.data, *args, **kwargs)
# use `raise_exception=True` to raise a ValidationError
serializer.is_valid(raise_exception=True)
response = super(BaseView, self).dispatch(request, *args, **kwargs)
return response
However, I would suggest against this, as other functionality of dispatch() probably should be performed prior to handling validation; thus, you could move the above logic to the relevant post/patch/put methods instead.
In these methods you can also use self.request directly since it was already initialized by dispatch().
I think drf-tracking does what you are looking for. You may want to check it out.
I don't think you're going about this the correct way. The best way to log the request, with validation is in your Authentication Class and add the audit log to the request.
Then you can use your APIView to log the render time, against the AuditLog generated in the Authentication Class.
Here's an example using Token Authentication, assuming each request has a header Authorization: Bearer <Token>.
settings.py
...
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'common.authentication.MyTokenAuthenticationClass'
),
...,
}
common/authentication.py
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from ipware.ip import get_real_ip
from rest_framework import authentication
from rest_framework import exceptions
from accounts.models import Token, AuditLog
class MyTokenAuthenticationClass(authentication.BaseAuthentication):
def authenticate(self, request):
# Grab the Athorization Header from the HTTP Request
auth = authentication.get_authorization_header(request).split()
if not auth or auth[0].lower() != b'bearer':
return None
# Check that Token header is properly formatted and present, raise errors if not
if len(auth) == 1:
msg = _('Invalid token header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid token header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
token = Token.objects.get(token=auth[1])
# Using the `ipware.ip` module to get the real IP (if hosted on ElasticBeanstalk or Heroku)
token.last_ip = get_real_ip(request)
token.last_login = timezone.now()
token.save()
# Add the saved token instance to the request context
request.token = token
except Token.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token.')
# At this point, insert the Log into your AuditLog table and add to request:
request.audit_log = AuditLog.objects.create(
user_id=token.user,
request_payload=request.body,
# Additional fields
...
)
# Return the Authenticated User associated with the Token
return (token.user, token)
Now, you have access to the AuditLog in your request. So you can log everything before and after validation.
I have 2 Flask apps (different projects) that work together . One implements some API which uses tokens for auth. The second one consumes the API and makes a web interface for it. Now I have a login function that sends the username and password to the API, and if correct, gets the auth token in return. Once I have the token, I save it to the session of the user and the user should now be considered as logged in/ autheticated. How can I implement the login_required decorator for such a case.
Here is my login function -
def login(self):
response = make_request(BASE_URL + 'login/', clean_data(self.data))
if response.status_code == 200:
session['auth_token'] = response.json().get('auth_token')
return True
return False
How can I make the login_required decorator?
Also I am using Redis to store sessions if that matters.
Have a look at the official flask docs regarding decorators:
https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/ or the python docs https://www.python.org/dev/peps/pep-0318/ as well.
Your decorator should look something like:
from functools import wraps
from flask import abort
import jwt
def authorize(f):
#wraps(f)
def decorated_function(*args, **kws):
if not 'Authorization' in request.headers:
abort(401)
user = None
data = request.headers['Authorization'].encode('ascii','ignore')
token = str.replace(str(data), 'Bearer ','')
try:
user = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])['sub']
except:
abort(401)
return f(user, *args, **kws)
return decorated_function
... and then in your app.py you may have:
#app.route('/api/game', methods=['POST'])
#authorize
def create(user):
data = json.loads(request.data)
....
In this particular case I have used JWT as token and your token can be different respectively the decoding of the token can be your custom implementation, but the basic mechanisms are pretty much as on the example above.
I would place the following decorator function in somewhere common
def validate_api_token(validation_func):
def decorator(f):
#wraps(f)
def decorated_function(*args, **kws):
api_token = request.headers.get('Authorization')
is_valid_api_token = validation_func(api_token)
if is_valid_api_token:
return f(*args, **kws)
return 'Invalid API Token', 401
return decorated_function
return decorator
For small POC flask apps, if you're ok with storing the tokens in a non-versioned file, the following can work:
# tokens are read from a non-versioned `.tokens` file and loaded into a set
api_tokens = load_api_tokens()
def simple_api_token_validation(api_token):
return api_token in api_tokens
#app.route("/v1/my/secret/function", methods=['POST'])
#validate_api_token(simple_api_token_validation)
def my_secret_function():
body = request.get_json()
# ...
Another simple option is to query against a database (e.g. redis):
redis_session = Redis(host=REDIS_HOST, password=REDIS_PASSWORD)
def redis_api_token_validation(api_token):
if not api_token:
return False
api_token_hash = hashlib.sha256(api_token.encode()).hexdigest()
return redis_session.exists(f'api:tokens:{api_token_hash}')
#app.route("/v1/my/secret/function", methods=['POST'])
#validate_api_token(redis_api_token_validation)
def my_secret_function():
body = request.get_json()
# ...
Best IMO as #Velin answered is to use jwt to validate the token
Given that each subsequent request will contain the API token, the decorator should do the following
Accept a generic request. You can use *args and **kargs for that
Extract the token from the header and compare it with the token stored in db (not Redis, but wherever the token generated is stored in the backend)
If authenticated, the *args and **kargs should be passed on to the decorated function
The output of the decorated function should then be returned as is
If the authentication failed, an error message should be returned.
For explanation on decorators, check out this link:
http://thecodeship.com/patterns/guide-to-python-function-decorators/
In an API built with Django REST Framework authentication can be done using the TokenAuthentication method. Its documentation says the authentication token should be sent via an Authorization header.
Often one can send API-keys or tokens via a query string in order to authenticate, like https://domain.com/v1/resource?api-key=lala.
Is there a way to do the same with Django REST Framework's TokenAuthentication?
By default DRF doesn't support query string to authenticate, but you can easily override their authenticate method in TokenAuthentication class to support it.
An example would be:
class TokenAuthSupportQueryString(TokenAuthentication):
"""
Extend the TokenAuthentication class to support querystring authentication
in the form of "http://www.example.com/?auth_token=<token_key>"
"""
def authenticate(self, request):
# Check if 'token_auth' is in the request query params.
# Give precedence to 'Authorization' header.
if 'auth_token' in request.query_params and \
'HTTP_AUTHORIZATION' not in request.META:
return self.authenticate_credentials(request.query_params.get('auth_token'))
else:
return super(TokenAuthSupportQueryString, self).authenticate(request)
class QueryStringBasedTokenAuthentication(TokenAuthentication):
def authenticate(self, request):
key = request.query_params.get('auth_token').strip()
if key:
return self.authenticate_credentials(key)
return False
DRF has TokenAuthentication which looks for token in header. The method authenticate_credentials takes care verifying the token.
As of 2018 using Django Rest Framework you can create your own Authentication class see http://getblimp.github.io/django-rest-framework-jwt/#extending-jsonwebtokenauthentication
class JSONWebTokenAuthenticationQS(BaseJSONWebTokenAuthentication):
def get_jwt_value(self, request):
return request.QUERY_PARAMS.get('jwt')
Then in the APIView class add
authentication_classes = (JSONWebTokenAuthenticationQS,)
Or
#authentication_classes((JSONWebTokenAuthenticationQS,))
On the view function.
Following up on Scott Warren's answer. That was a step in the right direction, because the DRFJWT docs don't include the important authentication_classes line. But with that, as noted in Issue 441 there is a big problem that you can't mix the JWT authentication methods. I ended up with:
class JSONWebTokenAuthenticationQS(JSONWebTokenAuthentication):
def get_jwt_value(self, request):
return request.GET.get('jwt') or JSONWebTokenAuthentication.get_jwt_value(self, request)
which so far seems to work well. It uses JSONWebTokenAuthentication instead of the Base class, because it has to in order to use the original get_jwt_value method.
refer answer of OmriToptix and Scott Warren and others website 1 and 2
for now most case use JSONWebTokenAuthentication, so now should override its get_jwt_value, full code is:
# from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.authentication import get_authorization_header
class JWTAuthByQueryStringOrHeader(JSONWebTokenAuthentication):
# class JWTAuthByQueryStringOrHeader(BaseJSONWebTokenAuthentication):
"""
Extend the TokenAuthentication class to support querystring authentication
in the form of "http://www.example.com/?jwt_token=<token_key>"
"""
def get_jwt_value(self, request):
# Check if 'jwt_token' is in the request query params.
# Give precedence to 'Authorization' header.
queryParams = request.query_params
reqMeta = request.META
if ('jwt_token' in queryParams) and ('HTTP_AUTHORIZATION' not in reqMeta):
jwt_token = queryParams.get('jwt_token')
# got jwt token from query parameter
return jwt_token
else:
# call JSONWebTokenAuthentication's get_jwt_value
# to get jwt token from header of 'Authorization'
return super(JWTAuthByQueryStringOrHeader, self).get_jwt_value(request)
here save above code to apps/util/jwt_token.py, then NOT FORGET add related Django settings:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'apps.util.jwt_token.JWTAuthByQueryStringOrHeader',
...
),
...
}
now frontend/web side can call api like this:
http://localhost:65000/api/v1/scripts/3d9e77b0-e538-49b8-8790-60301ca79e1d/script_word_export/?jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWVkMGEwZDgtMmFiYi00MDFkLTk5NTYtMTQ5MzcxNDIwMGUzIiwidXNlcm5hbWUiOiJsc2R2aW5jZW50IiwiZXhwIjoxNTMxOTAyOTU0LCJlbWFpbCI6InZpbmNlbnQuY2hlbkBuYXR1cmxpbmcuY29tIn0.wheM7Fmv8y8ysz0pp-yUHFqfk-IQ5a8n_8OplbYkj7s
to pass jwt_token into server side, get authorized to download/export the file.
while still support original method pass jwt token inside header 'Authorization':
POST http://localhost:65000/api/v1/scripts/3d9e77b0-e538-49b8-8790-60301ca79e1d/script_word_export/
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWVkMGEwZDgtMmFiYi00MDFkLTk5NTYtMTQ5MzcxNDIwMGUzIiwidXNlcm5hbWUiOiJsc2R2aW5jZW50IiwiZXhwIjoxNTMxOTAyOTU0LCJlbWFpbCI6InZpbmNlbnQuY2hlbkBuYXR1cmxpbmcuY29tIn0.wheM7Fmv8y8ysz0pp-yUHFqfk-IQ5a8n_8OplbYkj7s
I'm building a REST API with Flask which add a photo to a database. The database is abstracted in PhotoModel Class. The API receives a JSON formated HTTP POST which contain the picture in a bin string an the name, all the other parameters are optional.
How to construct "photo" object if some param aren't present in the JSON posted?
On the database Model (PhotoModel) I have specify only two compulsory items, so the logic to only take into account params present in the JSON should be in the function bellow.
def add_photo():
"""Add photo to database"""
if request.method == 'POST' and request.headers['Content-Type'] == 'application/json':
photo = PhotoModel(
name = request.json['name'],
device_version = request.json['device_version'],
date = request.json['date'],
picture = request.json['picture'],
comment = request.json['comment']
)
try:
photo.put()
return "200"
except CapabilityDisabledError:
return "500 DB read-only"
else:
return "415 Unsupported Media Type"
I can't figure out how to do it, any pointer would help
Take a look at peewee it comes with a RESTful API in JSON. It's also an light ORM engine.
I've discovered JSON Schema and it works fantastic to validate JSON requests.
Create a decorator which you can use for all views:
from functools import update_wrapper
from jsonschema import validate as jsonschema_validate
def validate(schema):
def decorator(f):
def wrapped_function(*args, **kwargs):
# Validate request Content Type
if request.json is None:
raise ValidationError("Content Type must be JSON")
# Validate document
jsonschema_validate(request.json, schema)
return f(*args, **kwargs)
return update_wrapper(wrapped_function, f)
return decorator
Use decorator for your views:
#app.route('/', methods=['POST'])
#validate(schema)
def insert_document():
# now your request.json object is validated against the specified schema
data = request.get_json() #You can use any method here.
#Below the required parameters outside the try
email=data['email']
role=data['role']
try:
#Here are the optional json parameters inside a try
firstname = data['firstname']
lastname = data['lastname']
except KeyError:
#Here handle the exception, maybe parse some default values.
pass