Flask-restful basic Authentication - python

I am new to Flask and I need some help for my school work.
I am trying to build a simple ToDo list system using flask-restful.
My current code looks like this:
class ToDoList(Resource):
'''TODO LIST'''
operation = ['delete']
decorators = [auth.login_required, advertise('operation')]
def post(self):
"""remove all item in the TODO list"""
operation = request.args.get('op')
if operation == 'delete':
collection2.delete_many({})
return {'Success': 'OK'}, 200
return {'Error':'Illegal Operation'}, 400
def get(self):
"""return a list of the TODO name"""
list_1 = collection2.find()
list_2 = []
for each in list_1:
list_2.append(JSONEncoder().encode(each))
return {'list':list_2}, 200
It works, but I want only the post method to require authentication, and get method without authentication so anyone can acquire the list without login. I am using the flask-restful I don't know how to give the decorators separately to each function.

I used flaskrestplus to do basic authentication. All the required authorizations are provided as an authorizations dictionary. Then they are passed to the API.
Also the authorizations can be applied at the method level using
#api.doc(security='basicAuth')
The validation logic (can be ldap validation or db validation) can be writted in a decorator called requires_Auth. This decorator is invoked using
decorators = [requires_Auth]
Complete code
from flask import Flask, request
from flask_restplus import Api, Resource
from functools import wraps
def requires_Auth(f):
#wraps(f)
def decorator(*args, **kwargs):
auth = request.authorization
if auth:
print "inside decorator", auth.username,auth.password
return f(*args, **kwargs)
else:
return "Login required!!!!",401
return decorator
authorizations = {
'basicAuth': {
'type': 'basic',
'in': 'header',
'name': 'Authorization'
}
}
api = Api(app, version='1.0',
authorizations=authorizations
)
ns = api.namespace('/', description='Authentication API')
#ns.route('/withDecorator')
class HelloWorldWithDecorator(Resource):
decorators = [requires_Auth]
#api.doc(security='basicAuth')
def get(self):
return {'hello': 'world'}
api.add_namespace(ns)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5001)

From Flask-RESTful documentation [1]:
Alternatively, you can specify a dictionary of iterables that map to HTTP methods and the decorators will only apply to matching requests.
def cache(f):
#wraps(f)
def cacher(*args, **kwargs):
# caching stuff
return cacher
class MyResource(restful.Resource):
method_decorators = {'get': [cache]}
def get(self, *args, **kwargs):
return something_interesting(*args, **kwargs)
def post(self, *args, **kwargs):
return create_something(*args, **kwargs)
In your case it would be:
method_decorators = {'post': [auth.login_required]}

Related

Django Keycloak integration flow

Try to follow the follow steps to integrate keycloak with Django system without using the builtin contrib.auth app.
My question is:
If I am not using the built-in User objects, how do I determine if a user is authenticated or not, by using the SSO session Keycloak generated?
Step 5, associate it with a session, what does it mean?
Thanks!
This might be a little late but could be helpful to someone in the future.
The contrib.auth is useful when you want to implement Django Authentication only.
The given approach assumes you are using the users from Keycloak and don't have a Django User Object to work with. If you are looking for later case, look at this package - https://django-keycloak.readthedocs.io/en/latest/
My usecase for integration of Keycloak with React Frontend and a REST DRF Backend. For integration with keycloak, you can make a custom middleware responsible for unpacking access_tokens generated from keycloak send in the Authorization Header on each request and validate permissions based on your client config file you need to export from keycloak from your respective client.
This is the middleware I used for my usecase
import re
import logging
from django.conf import settings
from django.http.response import JsonResponse
from django.utils.deprecation import MiddlewareMixin
from keycloak import KeycloakOpenID
from keycloak.exceptions import (
KeycloakInvalidTokenError,
)
from rest_framework.exceptions import (
PermissionDenied,
AuthenticationFailed,
NotAuthenticated,
)
logger = logging.getLogger(__name__)
class KeycloakMiddleware(MiddlewareMixin):
"""
Custom KeyCloak Middleware for Authentication and Authorization
"""
def __init__(self, get_response):
"""
:param get_response:
"""
super().__init__(get_response=get_response)
self.config = settings.KEYCLOAK_CONFIG
# Read configurations
try:
self.server_url = self.config["KEYCLOAK_SERVER_URL"]
self.client_id = self.config["KEYCLOAK_CLIENT_ID"]
self.realm = self.config["KEYCLOAK_REALM"]
except KeyError as keycloak_snippet_no_exist:
raise Exception(
"KEYCLOAK_SERVER_URL, KEYCLOAK_CLIENT_ID or KEYCLOAK_REALM not found."
) from keycloak_snippet_no_exist
self.client_secret_key = self.config.get("KEYCLOAK_CLIENT_SECRET_KEY", None)
self.client_public_key = self.config.get("KEYCLOAK_CLIENT_PUBLIC_KEY", None)
self.default_access = self.config.get("KEYCLOAK_DEFAULT_ACCESS", "DENY")
self.method_validate_token = self.config.get("KEYCLOAK_METHOD_VALIDATE_TOKEN", "INTROSPECT")
self.keycloak_authorization_config = self.config.get("KEYCLOAK_AUTHORIZATION_CONFIG", None)
# Create Keycloak instance
self.keycloak = KeycloakOpenID(
server_url=self.server_url,
client_id=self.client_id,
realm_name=self.realm,
client_secret_key=self.client_secret_key,
)
# Read policies
if self.keycloak_authorization_config:
self.keycloak.load_authorization_config(self.keycloak_authorization_config)
# Django
self.get_response = get_response
#property
def keycloak(self):
"""
Getter KeyCloak Instance
"""
return self._keycloak
#keycloak.setter
def keycloak(self, value):
self._keycloak = value
#property
def config(self):
"""
Getter Config Instance
"""
return self._config
#config.setter
def config(self, value):
self._config = value
#property
def server_url(self):
"""
Getter Server URL
"""
return self._server_url
#server_url.setter
def server_url(self, value):
self._server_url = value
#property
def client_id(self):
"""
Getter Client_ID of KeyCloak Client
"""
return self._client_id
#client_id.setter
def client_id(self, value):
self._client_id = value
#property
def client_secret_key(self):
"""
Getter Client Secret Key
"""
return self._client_secret_key
#client_secret_key.setter
def client_secret_key(self, value):
self._client_secret_key = value
#property
def client_public_key(self):
"""
Getter Client Public Key
"""
return self._client_public_key
#client_public_key.setter
def client_public_key(self, value):
self._client_public_key = value
#property
def realm(self):
"""
Getter KeyClaok Realm
"""
return self._realm
#realm.setter
def realm(self, value):
self._realm = value
#property
def keycloak_authorization_config(self):
"""
Getter KeyCloak Authorization Config
"""
return self._keycloak_authorization_config
#keycloak_authorization_config.setter
def keycloak_authorization_config(self, value):
self._keycloak_authorization_config = value
#property
def method_validate_token(self):
"""
Getter Validate Token Private Method
"""
return self._method_validate_token
#method_validate_token.setter
def method_validate_token(self, value):
self._method_validate_token = value
def __call__(self, request):
"""
:param request:
:return:
"""
return self.get_response(request)
def process_view(self, request, view_func, view_args, view_kwargs):
# pylint: disable=unused-argument
"""
Validate only the token introspect.
:param request: django request
:param view_func:
:param view_args: view args
:param view_kwargs: view kwargs
:return:
"""
if hasattr(settings, "KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS"):
path = request.path_info.lstrip("/")
if any(re.match(m, path) for m in settings.KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS):
logger.debug("** exclude path found, skipping")
return None
try:
view_scopes = view_func.cls.keycloak_scopes
except AttributeError as _keycloak_attribute_error:
logger.debug(
"Allowing free acesss, since no authorization configuration (keycloak_scopes) \
found for this request route :%s",
request,
)
return None
if "HTTP_AUTHORIZATION" not in request.META:
return JsonResponse(
{"detail": NotAuthenticated.default_detail},
status=NotAuthenticated.status_code,
)
auth_header = request.META.get("HTTP_AUTHORIZATION").split()
token = auth_header[1] if len(auth_header) == 2 else auth_header[0]
# Get default if method is not defined.
required_scope = (
view_scopes.get(request.method, None)
if view_scopes.get(request.method, None)
else view_scopes.get("DEFAULT", None)
)
# DEFAULT scope not found and DEFAULT_ACCESS is DENY
if not required_scope and self.default_access == "DENY":
return JsonResponse(
{"detail": PermissionDenied.default_detail},
status=PermissionDenied.status_code,
)
try:
# >>>>>>> Added Options kwargs to verify decode permissions to django middleware
options = {
"verify_signature": True,
"verify_aud": False,
"verify_exp": True,
}
user_permissions = self.keycloak.get_permissions(
token,
method_token_info=self.method_validate_token.lower(),
key=self.client_public_key,
options=options,
)
except KeycloakInvalidTokenError as _keycloak_invalid_token_error:
return JsonResponse(
{"detail": AuthenticationFailed.default_detail},
status=AuthenticationFailed.status_code,
)
for perm in user_permissions:
if required_scope in perm.scopes:
return None
# User Permission Denied
return JsonResponse(
{"detail": PermissionDenied.default_detail},
status=PermissionDenied.status_code,
)
With this approach, you can define policy authorization straight inside your view class similar to this.
class GetMockData(APIView):
"""
Another GET API to Fetch fake Data
"""
keycloak_scopes = {"GET": "medicine:view"}
def get(self, request: HttpRequest) -> HttpResponse:
"""
V1 API to get some medicine data from the database
"""
data = get_all_medicines()
if not data:
res = CustomResponse(
success=False,
payload=None,
error=E_RANGE_MESSAGE,
status=status.HTTP_404_NOT_FOUND,
)
return res.send_response()
logger.warning("Testing a fake warning >>>>>>>> WARNING <<<<<<<<?")
data = MedicineSerializer(data, many=True).data
res = CustomResponse(success=True, payload=data)
return res.send_response()

Python | check if dict{} item is in list[]

Attempting to check an in-memory list, plant_list[] against a JSON payload from an api.
If the incoming payload's dict name matches inside of plant_list the if should fire off.
Instead my script only returns null
Please point out my mistakes.
The JSON sent over the api call is:
{ "name":"swizz", "days": "7", "price": 2.00 }
Source Code
from flask import Flask, request
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
#app.route('/', methods=['GET', 'POST'])
def home():
return 'Tiny'
plant_list = []
class Plant(Resource):
def get(self, name):
return {'Name':name}, 200
def post(self, name):
payload = request.get_json()
for x in range(0, len(plant_list)):
if payload['name'] == plant_list[x]['name']:
return {'message': f'''Item {payload['name']} already stored in database.'''}
else:
plant_list.append(payload)
return plant_list, 201
api.add_resource(Plant, '/plant/<string:name>')
if __name__ == '__main__':
app.run(port=9004, debug=True)
You can test for key in dict simply by: if key_name in my_dict:... So, if "price" in plant_list[x]:
You are setting plant_list as a global. If you want to use that inside a Class, you should define it inside the function in your class:
plant_list = []
class Plant(Resource):
....
def post(self, name):
global plant_list
....
Not sure why you need it as a global variable. Perhaps you want:
class Plant(Resource):
plant_list = []
....
def post(self, name):
....

flask: How to store and retrieve POST calls?

#app.route('/path/<user>/<id>', methods=['POST'])
#cache.cached(key_prefix='/path/<user>', unless=True)
def post_kv(user, id):
cache.set(user, id)
return value
#app.route('/path/<user>', methods=['GET'])
#cache.cached(key_prefix='/path/<user>', unless=True)
def getkv(user):
cache.get(**kwargs)
I want to be able to make POST calls to the path described, store them, and GET their values from the user. The above code runs, but has bugs and doesn't perform as needed. Frankly, the flask-cache docs aren't helping. How can I properly implement cache.set and cache.get to perform as needed?
In Flask, implementing your custom cache wrapper is very simple.
from werkzeug.contrib.cache import SimpleCache, MemcachedCache
class Cache(object):
cache = SimpleCache(threshold = 1000, default_timeout = 100)
# cache = MemcachedCache(servers = ['127.0.0.1:11211'], default_timeout = 100, key_prefix = 'my_prefix_')
#classmethod
def get(cls, key = None):
return cls.cache.get(key)
#classmethod
def delete(cls, key = None):
return cls.cache.delete(key)
#classmethod
def set(cls, key = None, value = None, timeout = 0):
if timeout:
return cls.cache.set(key, value, timeout = timeout)
else:
return cls.cache.set(key, value)
#classmethod
def clear(cls):
return cls.cache.clear()
...
#app.route('/path/<user>/<id>', methods=['POST'])
def post_kv(user, id):
Cache.set(user, id)
return "Cached {0}".format(id)
#app.route('/path/<user>', methods=['GET'])
def get_kv(user):
id = Cache.get(user)
return "From cache {0}".format(id)
Also, the simple memory cache is for single process environments and the SimpleCache class exists mainly for the development server and is not 100% thread safe. In production environments you should use MemcachedCache or RedisCache.
Make sure you implement logic when item is not found in the cache.

DRY validation in Bottle?

My Bottle apps haven't been very DRY, here's a test-case:
from uuid import uuid4
from bottle import Bottle, response
foo_app = Bottle()
#foo_app.post('/foo')
def create():
if not request.json:
response.status = 400
return {'error': 'ValidationError', 'error_message': 'Body required'}
body = request.json
body.update({'id': uuid4().get_hex())
# persist to db
# ORM might set 'id' on the Model layer rather than setting it here
# ORM will validate, as will db, so wrap this in a try/catch
response.status = 201
return body
#foo_app.put('/foo/<id>')
def update(id):
if not request.json:
response.status = 400
return {'error': 'ValidationError', 'error_message': 'Body required'}
elif 'id' not in request.json:
response.status = 400
return {'error': 'ValidationError', 'error_message': '`id` required'}
db = {} # should be actual db cursor or whatever
if 'id' not in db:
response.status = 404
return {'error': 'Not Found',
'error_message': 'Foo `id` "{id}" not found'.format(id)}
body = request.json
# persist to db, return updated object
# another try/catch here in case of update error (from ORM and/or db)
return body
One way of solving this issue is to have a global error handler, and raise errors all over the place.
Another is to use decorators, which also have overhead issues.
Is there a better way of doing the validation side of each route? - I'm thinking of something like:
foo_app.post('/foo', middleware=[HAS_BODY_F, ID_IN_DB_F])
Ended up finding Bottle's built-in "middleware" called "plugins" (reference):
from bottle import Bottle, request, response
app = Bottle()
def has_body(f):
def inner(*args, **kwargs):
if request.json:
return f(*args, **kwargs)
response.status = 400
return {'error': 'ValidationError',
'error_message': 'Body is required (and must be JSON).'}
return inner
def body_req(required):
def body_req_middleware(f):
def inner(*args, **kwargs):
intersection = required.intersection(set(request.json.keys()))
if intersection != required:
response.status = 400
return {'error': 'ValidationError',
'error_message': 'Key(s): {} are not in JSON payload'
''.format(', '.join('{!r}'.format(k)
for k in required - intersection))}
return f(*args, **kwargs)
return inner
return body_req_middleware
#app.post('/foo', apply=(has_body, body_req({'id', 'f'})))
def get_foo():
return {'foo': 'bar'}
Here is my solution to DRY validation, ended up with a decorator:
from itertools import imap, ifilter
from bottle import Bottle, request, response
app = Bottle()
middleware = lambda functions: lambda caller: lambda *args, **kwargs: next(
ifilter(None, imap(lambda g: g(*args, **kwargs), functions)),
caller(*args, **kwargs)
)
def has_body(*args, **kwargs):
if not request.json:
response.status = 400
return {'error': 'ValidationError',
'error_message': 'Body is required (and must be JSON).'}
def body_req(required):
def inner(*args, **kwargs):
intersection = required.intersection(set(request.json.keys()))
if intersection != required:
response.status = 400
return {'error': 'ValidationError',
'error_message': 'Key(s): {} are not in JSON payload'.format(
', '.join(imap(lambda key: "'{key}'".format(key=key),
required - intersection)))}
return inner
#app.post('/foo')
#middleware([has_body, body_req({'id', 'f'})])
def get_foo():
return {'foo': 'bar'}

Django unit test. Simple example

I learn unit test with Django. How to write test for this function? I need this example to understand.
#login_required
def datas(request):
queryset = Data.objects.filter(user=request.user)
if queryset.count() == 0:
return redirect('/data/')
return render_to_response('data_list.html',
{'data': queryset},
context_instance=RequestContext(request))
#imports here
class YourTestCase(TestCase):
fixtures = ['user-data.json']
def setUp(self):
self.client = Client()
def test_empty_datas(self):
self.client.login(username='something', password='something')
response = self.client.get('/path/to/view/') # or reverse by name
self.assertEqual(response.status_code, 302,
'View did not redirect on empty queryset.')
def test_populated_datas(self):
self.client.login(username='something', password='something')
Data.objects.create(some_field=some_value)
response = self.client.get('/path/to/view/') # or reverse by name
self.assertEqual(response.status_code, 200,
'View did not return a 200.')
...and so on. user-data would need to contain at least one user, otherwise you won't be able to authenticate.

Categories