How can I restrict who has access to the GraphiQL API browser with graphene-django? - permissions

Graphene-Django docs note that you can pass graphiql=False when instantiating the GraphQLView if you do not want to use the GraphiQL API browser. However, I'd like to keep the GraphiQL API browser available, and merely restrict who has access to it. How can that be done?
For instance, how would I make it so that only "staff" users (who can access the Admin site) have permission to access the GraphiQL browser?

You can extend the Graphene-Django GraphQLView and override its can_display_graphiql method (defined here) to add this sort of logic.
from graphene_django.views import GraphQLView as BaseGraphQLView
class GraphQLView(BaseGraphQLView):
#classmethod
def can_display_graphiql(cls, request, data):
# Only allow staff users to access the GraphiQL interface
if not request.user or not request.user.is_staff:
return False
return super().can_display_graphiql(request, data)
Then in your urls.py file, use your new GraphQLView instead of the default one:
# import the GraphQLView defined above
urlpatterns = [
# ...
path("graphql", GraphQLView.as_view(graphiql=True)),
]

Related

Flask JWT to SQLAlchemy User Object?

I have an app where the user details are passed as a JWT containing information about the current user and it's roles.
Everytime the user is logged in (via a KeyCloak instance), the information from the JWT is parsed on my end in a function that updates the user object via SQLAlchemy. However, since there is no user object being passed back and forth in the backend, I have to parse the JWT for roles for every action that requires it. I also have a need for auditing, and due to the structure of the app, this module does not necessarily have access to the request objects at the time of logging.
I'm looking for a neat way to make something like flask_users current_user() functionality work by mapping JWT -> ORM user object, to be able to transparently get the current user. Is there any way to go about this? The user registration and so on is completely separate from the app, and Flask only knows which user it is based on tokens in the requests that are being sent.
TLDR; Is there a way to load a user from the DB based on an already issued JWT (which contains information to map to a user), and is there perhaps already a lib or extension to flask that supports this?
I use a decorator to parse the JWT token using pyjwt.
Then from the parsed token you can get the user and do the proper authorization.
If you don't want to add the decorator to all your functions that require authorization you can use Flasks before_request.
from functools import wraps
from flask import Response, current_app, request
from jwt import decode
from jwt.exceptions import (DecodeError, ExpiredSignatureError,
InvalidSignatureError)
def authorize(func):
#wraps(func)
def check_authorization(*args, **kwargs):
try:
jwt_token = request.cookies.get('auth_token') # get token from request
if jwt_token is None:
return Response(status=401, headers={'WWW-Authenticate': 'Bearer'})
token = decode(
jwt_token,
key='pub_key', # public key to validate key
algorithms=['RS256'], # list of algs the key could be signed
verify=True
)
# you can call another function to do check user roles here
# e.g authorize(token['sub'])
return func(*args, **kwargs)
except (InvalidSignatureError, DecodeError, ExpiredSignatureError):
return Response(
response='{ "error": "token_invalid"}',
status=401,
headers={'WWW-Authenticate': 'Bearer'})
return check_authorization
This is supported with flask-jwt-extended: https://flask-jwt-extended.readthedocs.io/en/stable/complex_objects_from_token/

How to make a controller on Odoo for custom value?

I need to make a custom controller on Odoo for getting information from the particular task. And I can able to produce the result also. But now I'm facing an issue.
The client needs to retrieve the information with a particular field.
For example,
The client needs to retrieve the information with the tracking number and the data must be JSON format also. If the tracking number is 15556456356, the url should be www.customurl.com/dataset/15556456356
The route of that URL should be #http.route('/dataset/<string:tracking_number>', type='http or json', auth="user or public"), basically the method should be like this:
import json
from odoo import http
from odoo.http import Response, request
class tracking(http.Controller):
# if user must be authenticated use auth="user"
#http.route('/dataset/<string:tracking_number>', type='http', auth="public")
def tracking(self, tracking_number): # use the same variable name
result = # compute the result with the given tracking_number and the result should be a dict to pass it json.dumps
return Response(json.dumps(result), content_type='application/json;charset=utf-8',status=200)
This method accept http request and return a json response, if the client is sending a json requests you should change type='json'. don't forget to import the file in the __init___.py.
Lets take an example let say that I want to return some information about a sale.order by a giving ID in the URL:
import json
from odoo import http
from odoo.http import Response, request
class Tracking(http.Controller):
#http.route('/dataset/<int:sale_id>', type='http', auth="public")
def tracking(self, sale_id):
# get the information using the SUPER USER
result = request.env['sale.order'].sudo().browse([sale_id]).read(['name', 'date_order'])
return Response(json.dumps(result), content_type='application/json;charset=utf-8',status=200)
So when I enter this URL using my Browser: http://localhost:8069/dataset/1:

how to use django REST JWT authorization and authentication in class based views

I am using JWT authentication I am using this type of authorization app wide.
I am trying to figure out how to ue it in a view.
Example. Say I only want to allow a user to create an approved venue if they have the correct permissions. What would I add to this view to get access to the user?
I know that django has request.user but how do I turn that on? Is it always on and request.user is null if there is no token passed into the header? Or is it middleware? The problem I am ultimately having is there is a lot of info getting to this point, but very little on actually using the JWT on the view.
please help.
# for creating an approved venue add ons later
class CreateApprovedVenue(CreateAPIView):
queryset = Venue.objects.all()
serializer_class = VenueSerializer
Django rest framework jwt docs
https://jpadilla.github.io/django-rest-framework-jwt/
Django rest framework permissions docs
http://www.django-rest-framework.org/api-guide/permissions/
so I discovered this resource and looking at it now.
https://code.tutsplus.com/tutorials/how-to-authenticate-with-jwt-in-django--cms-30460
This example is sheading light:
# users/views.py
class CreateUserAPIView(APIView):
# Allow any user (authenticated or not) to access this url
permission_classes = (AllowAny,)
def post(self, request):
user = request.data
serializer = UserSerializer(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
To use JWT authentication, you need to do the following installation steps: https://jpadilla.github.io/django-rest-framework-jwt/#installation
Once that is done, you can include the auth by simply adding the authentication_classes as follows
# for creating an approved venue add ons later
class CreateApprovedVenue(CreateAPIView):
authentication_classes = (JSONWebTokenAuthentication, )
queryset = Venue.objects.all()
serializer_class = VenueSerializer
And you have user available to you as request.user in all the request methods. In the case of the CreateAPIView you can do:
def post(self, request, *args, **kwargs):
user = request.user
...

Django-Rest-Framework: How to Document GET-less Endpoint?

My co-worker implemented an API that only allows GET requests with an ID parameter (so I can GET /foo/5 but can't GET /foo/). If I try to access the API's endpoint without providing an ID parameter, it (correctly) throws an unimplemented exception.
I want to fix this endpoint to show its documentation when viewed, without an ID, over the web. However, I still want it to throw an exception when that endpoint is accessed programatically.
As I remember it, django-rest-framework is capable of distinguishing those two cases (via request headers), but I'm not sure how to define the endpoint such that it returns either documentation HTML or an exception as appropriate.
Can anyone help provide the pattern for this?
Based on the description, I would guess that the endpoint is a function based view, which is registered on a route where it listens for get requests WITH parameters. I would suggest to register another route where you will listen for get requests without parameters...
from rest_framework.decorators import api_view
from rest_framework import status
#api_view(['GET'])
def existing_get_item_api(request, item_id, *args, **kwargs):
# query and return the item here ...
pass
#api_view(['GET'])
def get_help(request, *args, **kwargs):
# compose the help
return Response(data=help, status = status.HTTP_200_OK)
# somewhere in urls.py
urlpatterns = [
url(r'api/items/(?P<item_id>[0-9]+)/', existing_get_item_api),
url(r'api/items/', get_help),
]
Let me know how is this working out for you.
We can user modelviewsets and routers for this implementation
viewsets.py
class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
http_method_names = ['GET']
queryset = Account.objects.all()
serializer_class = AccountSerializer
routers.py
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'accounts', AccountViewSet)

Google Analytics Management API - Insert method - Insufficient permissions HTTP 403

I am trying to add users to my Google Analytics account through the API but the code yields this error:
googleapiclient.errors.HttpError: https://www.googleapis.com/analytics/v3/management/accounts/**accountID**/entityUserLinks?alt=json returned "Insufficient Permission">
I have Admin rights to this account - MANAGE USERS. I can add or delete users through the Google Analytics Interface but not through the API. I have also added the service account email to GA as a user. Scope is set to analytics.manage.users
This is the code snippet I am using in my add_user function which has the same code as that provided in the API documentation.
def add_user(service):
try:
service.management().accountUserLinks().insert(
accountId='XXXXX',
body={
'permissions': {
'local': [
'EDIT',
]
},
'userRef': {
'email': 'ABC.DEF#gmail.com'
}
}
).execute()
except TypeError, error:
# Handle errors in constructing a query.
print 'There was an error in constructing your query : %s' % error
return None
Any help will be appreciated. Thank you!!
The problem was I using a service account when I should have been using an installed application. I did not need a service account since I had access using my own credentials.That did the trick for me!
Also remember that you have to specify the scope you would like to use, this example here (using the slightly altered example by Google) defines by default two scopes which would NOT allow to insert users (as they both give read only permissions) and would result in "Error 403 Forbidden" trying so.
The required scope is given in the code below:
from apiclient.discovery import build
from googleapiclient.errors import HttpError
from oauth2client.service_account import ServiceAccountCredentials
def get_service(api_name, api_version, scopes, key_file_location):
"""Get a service that communicates to a Google API.
Args:
api_name: The name of the api to connect to.
api_version: The api version to connect to.
scopes: A list auth scopes to authorize for the application.
key_file_location: The path to a valid service account JSON key file.
Returns:
A service that is connected to the specified API.
"""
credentials = ServiceAccountCredentials.from_json_keyfile_name(
key_file_location, scopes=scopes)
# Build the service object.
service = build(api_name, api_version, credentials=credentials)
return service
def get_first_profile_id(service):
# Use the Analytics service object to get the first profile id.
# Get a list of all Google Analytics accounts for this user
accounts = service.management().accounts().list().execute()
if accounts.get('items'):
# Get the first Google Analytics account.
account = accounts.get('items')[0].get('id')
# Do something, e.g. get account users & insert new ones
# ...
def main():
# Define the auth scopes to request.
# Add here
# https://www.googleapis.com/auth/analytics.manage.users
# to be able to insert users as well:
scopes = [
'https://www.googleapis.com/auth/analytics.readonly',
'https://www.googleapis.com/auth/analytics.manage.users.readonly',
]
key_file_location = 'my_key_file.json'
# Authenticate and construct service.
service = get_service(
api_name='analytics',
api_version='v3',
scopes=scopes,
key_file_location=key_file_location)
profile_id = get_first_profile_id(service)
print_results(get_results(service, profile_id))
if __name__ == '__main__':
main()
Regards,
HerrB92