How to use JWT with RESTful?
It works
#app.route('/test_route')
#jwt_required()
def protected():
return '%s' % current_identity
But what to do in this case?
api.add_resource(testApi, '/test_api')
I cannot find this case in the Flask-JWT documentation
You can try out something like this:
class TestApi(Resource):
#jwt_required()
def get(self):
return '%s' % current_identity
later use this:
api.add_resource(TestApi, '/test_api')
Related
I am only beginner in flask. Trying to integrate marshmallow and webargs. It perfectly works in flask-restful Resource class. But when I use a simple flask route it does not work
routes.py
class UserAPI(Resource):
#use_args(UserSchema())
def post(self, *args):
print(args)
return 'success', 201
def get(self):
return '<h1>Hello</h1>'
#bp.route('/test/', methods=['POST'])
#use_kwargs(UserSchema())
def test2(*args, **kwargs):
print(args)
print(kwargs)
return 'success', 201
api.add_resource(UserAPI, '/', endpoint='user')
I've added error handler which is necessary when using use_args
from webargs.flaskparser import parser, abort
from webargs import core
#parser.error_handler
def webargs_validation_handler(error, req, schema, *, error_status_code, error_headers):
status_code = error_status_code or core.DEFAULT_VALIDATION_STATUS
abort(
400,
exc=error,
messages=error.messages,
)
That's what I'm getting when I make request to Resource endpoint what is normal
And that's what I'm getting when I make request to a simple flask route what is not normal
I want to be able to use both ways
Found answer in webargs docs :)
https://webargs.readthedocs.io/en/latest/framework_support.html#error-handling
from flask import jsonify
# Return validation errors as JSON
#app.errorhandler(422)
#app.errorhandler(400)
def handle_error(err):
headers = err.data.get("headers", None)
messages = err.data.get("messages", ["Invalid request."])
if headers:
return jsonify({"errors": messages}), err.code, headers
else:
return jsonify({"errors": messages}), err.code
I'm building a REST-ful API using Django Rest Framework. I have handled integration with REST APIs before for work and have been given test API keys specifically for use with testing for integration, keys that allow you to send data and returns mock data, but doesn't put the data in the system. I am trying to implement this same feature in my API, but I haven't been able to find a way to do so yet.
you can use some decorator to do that :)
from django.contrib.auth import authenticate
def mock_funcname(func):
def wrapper(request, *args, **kwargs):
user = authenticate(token=request.REQUEST['token'])
if user.id == 'TEST USER ID':
return Response({'detail': "Some stuff mocked for this user :)"})
return func(request, *args, **kwargs)
return wrapper
So now you can put it like other decorator :
#api_view(['POST'])
#permission_classes((IsAuthenticated,))
#mock_funcname
def funcname(request, pid=None):
return response......
Its a basic function you need to make it more complicate
First of all, you can install mommy to create mock data in your tests.
So in your tests.py you can create a test to your API, checking the status_code and the response:
def test(self):
user = mommy.make(User) # Create a fake user
token = Token.objects.get(user__username=user.username) # Generate a token
self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) # Token for your request
response = self.client.post("url", your_data) # POST in your URL
self.assertEqual(response.status_code, status.HTTP_201_CREATED) # Status Code of Creation
self.assertEqual(User.objects.last().first_name, user.first_name) # Check the fields created
and then run the tests running python manage.py test or follow the documetation in DRF
I'm having problems with creating an access logger for my tastypie restfull app.
I'd like to log several HTTP headers in requests to server and pass them to logger/handler defined in django Settings file. The idea is to log every HTTP request into a access log file.
I've encountered several logging modules (apps) but they all use database, I want something simpler for a basic access log.
I ended up making my own Middleware class in middleware.py inside application root.
Also I placed 'appname.middleware.RequestLoggerMiddleware', inside Settings.py Middleware section.
Here is the code for my access logging middleware class:
import logging
logger = logging.getLogger('access')
class RequestLoggerMiddleware(object):
def process_request(self, request):
... logging logic here...
logger.info('logging message'))
return None
For more info about Middleware components see Django Middleware documentation.
Another possibility is to override of the ModelResource.dispatch() method, in a custom ModelResource object:
class CustomModelResource(ModelResource):
def dispatch(self, request_type, request, **kwargs):
"""
Override for systematic logging.
"""
log_user = request.META['USER']
log_request_type = request_type
log_resource_name = kwargs['resource_name']
log_api_name = kwargs['api_name']
log_response = {}
try:
response = super(CustomModelResource, self).dispatch(request_type, request, **kwargs)
log_response['response_code'] = response.status_code
# Also log what could go wrong
except Exception, e:
log_response['error_type'] = e.__class__.__name__
log_response['error_message'] = e.message
log_response['response_code'] = http.HttpBadRequest.status_code
raise
finally:
# Log all the things
logger.debug('%s asked for %s on %s through api %s: \n%s' % (
log_user,
log_request_type,
log_resource_name,
log_api_name,
log_response,
))
return response
class Meta:
# Other custom stuff
import logging
logger = logging.getLogger('project.app.view')
def my_view(request):
entry = '%s %s for %s' % (request.method, request.get_full_path(), request.META['REMOTE_ADDR'])
logger.info(entry)
See https://docs.djangoproject.com/en/1.4/topics/logging/
everyone. I am trying to write tests for RESTful API implemented using django-tastypie with http basic auth. So, I have the following code:
def http_auth(username, password):
credentials = base64.encodestring('%s:%s' % (username, password)).strip()
auth_string = 'Basic %s' % credentials
return auth_string
class FileApiTest(TestCase):
fixtures = ['test/fixtures/test_users.json']
def setUp(self):
self.extra = {
'HTTP_AUTHORIZATION': http_auth('testuser', 'qwerty')
}
def test_folder_resource(self):
response = self.client.get('/api/1.0/folder/', **self.extra)
self.assertEqual(response.status_code, 200)
def test_folder_resource_post(self):
response = self.client.post('/api/1.0/folder/', **self.extra)
self.assertNotEqual(response.status_code, 401)
GET request is done well, returning status code 200. But POST request always returns 401. I am sure I am doing something wrong. Any advice?
Check out this question. I've used that code for tests using both GET and POST and it worked. The only difference I can see is that you have used base64.encodestring instead of base64.b64encode.
Otherwise, if that doesn't work, how are you performing the HTTP Authentication? I wrote and use this function decorator:
import base64
from django.http import HttpResponse
from django.contrib.auth import authenticate, login
def http_auth(view, request, realm="", must_be='', *args, **kwargs):
if 'HTTP_AUTHORIZATION' in request.META:
auth = request.META['HTTP_AUTHORIZATION'].split()
if len(auth) == 2:
if auth[0].lower() == "basic":
uname, passwd = base64.b64decode(auth[1]).split(':')
if must_be in ('', uname):
user = authenticate(username=uname, password=passwd)
if user is not None and user.is_active:
login(request, user)
request.user = user
return view(request, *args, **kwargs)
# They mustn't be logged in
response = HttpResponse('Failed')
response.status_code = 401
response['WWW-Authenticate'] = 'Basic realm="%s"' % realm
return response
def http_auth_required(realm="", must_be=''):
""" Decorator that requires HTTP Basic authentication, eg API views. """
def view_decorator(func):
def wrapper(request, *args, **kwargs):
return http_auth(func, request, realm, must_be, *args, **kwargs)
return wrapper
return view_decorator
I've found a reason of my problem. DjangoAuthorization checks permissions with django premissions framework, since I don't use it in my project — all post/put/delete requests from non superuser are unauthorized. My bad.
Anyway, thanks a lot to you, guys, for responses.
On Python 3
#staticmethod
def http_auth(username, password):
"""
Encode Basic Auth username:password.
:param username:
:param password:
:return String:
"""
data = f"{username}:{password}"
credentials = base64.b64encode(data.encode("utf-8")).strip()
auth_string = f'Basic {credentials.decode("utf-8")}'
return auth_string
def post_json(self, url_name: AnyStr, url_kwargs: Dict, data: Dict):
"""
Offers a shortcut alternative to doing this manually each time
"""
header = {'HTTP_AUTHORIZATION': self.http_auth('username', 'password')}
return self.post(
reverse(url_name, kwargs=url_kwargs),
json.dumps(data),
content_type="application/json",
**header
)
Because I didn't want to use Django's in-build authentication system (maybe I should do, please tell me if this is the case), I created a simple little auth class:
import random
import hashlib
from myapp import models
class CustomerAuth:
key = 'customer'
def __init__(self, session):
self.session = session
def attempt(self, email_address, password):
password_hash = hashlib.sha1(password).hexdigest()
try:
return models.Customer.objects.get(
email_address=email_address,
password_hash=password_hash)
except models.Customer.DoesNotExist:
return None
def login(self, customer):
self.session[self.key] = customer
def logout(self):
if self.session.has_key(self.key):
self.session[self.key] = None
def is_logged_in(self):
return self.session.has_key(self.key)
and self.session[self.key] != None
def get_active(self):
if self.is_logged_in():
return self.session[self.key]
else:
raise Exception('No user is logged in.')
def redirect_to_login(self):
return HttpResponseRedirect('/login/')
def redirect_from_login(self):
return HttpResponseRedirect('/account/')
The problem is, that when I want to use it to stop unauthorized access, I have to use this code snippet in every single view method:
def example(req):
auth = CustomerAuth(req.session)
if not auth.is_logged_in():
return auth.redirect_to_login()
As you can imagine, this yields fairly ugly and repetitive code. What is a better way of doing this? Should I be using Django's auth framework?
Firstly, yes you should use Django's authentication framework, and build your own custom auth backend.
Secondly, however you do it, you'll need to have something in the views that you want to restrict access to. The best way to do that is via a decorator on the view. Again, Django's built-in framework gives you access to the #login_required decorator, which does exactly what you want.