i am using keycloak as IAM for my project which is a multi service project.
i want to use django rest framework for my django service.
i have managed to make authentication work and request.user returns an instance of User from django.contrib.auth.models, the problem is drf creates a new request object for the view, and the user authenticated by keycloak backend set in AUTHENTICATION_BACKENDS is lost,
to solve this problem i have made a permission class like the following:
class ClientRoleBasedPermission(permissions.BasePermission):
role = "create_room"
def has_permission(self, request, view):
if self.role in request._request.user.get_all_permissions():
return True
return False
i am using this simple view to test things
class ListUsers(APIView):
"""
View to list all users in the system.
* Requires token authentication.
* Only admin users are able to access this view.
"""
permission_classes = [CreateRoomPermission]
permission = "create_room"
def get(self, request, format=None):
"""
Return a list of all users.
"""
usernames = [user.username for user in User.objects.all()]
return Response(usernames)
authentication backend i am using :KeycloakAuthorizationCodeBackend
is there a better way to solve this issue ?
Related
I have three APIs created by inheriting APIView class
class API1View(APIView):
def get(self, request):
return Response("API 1 view")
class API2View(APIView):
def get(self, request):
return Response("API 2 view")
class API3View(APIView):
def get(self, request):
return Response("API 3 view")
I have three users
user1
user2
user3
I three kind of users.
customer
manager
employee
Different kind of users should have access to different APIs
The Manager can only able to view the API1 and API2
The Customer can only able to view the API1
The Employee can only able to view the API3
In future, I need to able to revoke access of a certain user to certain API.
Is it good to use Django Groups or is there any better ways to do it?
Or is there any DRF way to do it? (I have seen DRF permissions but I'm not sure if that will work for this scenario.)
You can create your custom permissions and use them in your APIs:
from rest_framework.permissions import BasePermission
class CustomerPermission(BasePermission):
message = 'You Don\'t Have Access To Customer APIs'
def has_permission(self, request, view):
return bool(request.user.is_customer)
Usage:
class API3View(APIView):
permission_classes = [CustomerPermission]
def get(self, request):
return Response("API 3 view")
The permission_classes gets multiple permissions but it works like
an and if you want to set access to an API for multiple users, you
should write custom permission for it too
Graphene provides a GraphQL integration into Django and supplies a view to create a URL endpoint. The question is how can the endpoint be protected for API usage? The recommend method is to use a LoginRequiredMixin which is great for logged in users, but not for use as an API.
I've tried integrating it with DRF tokens, but still end up with the session middleware requiring CSRF. The only solution that works is adding a CSRF exempt decorator, but I fear that this opens up a security vulnerability.
# urls.py
path("graphiql/", root_views.SessionGraphQLView.as_view(graphiql=True), name="graphiql"),
path("graphql/", root_views.TokenGraphQLView.as_view(graphiql=False), name="graphql"),
# views.py
class TokenLoginRequiredMixin(AccessMixin):
"""A login required mixin that allows token authentication."""
def dispatch(self, request, *args, **kwargs):
"""If token was provided, ignore authenticated status."""
http_auth = request.META.get("HTTP_AUTHORIZATION")
if http_auth and "Token" in http_auth:
pass
elif not request.user.is_authenticated:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
#method_decorator(csrf_exempt, name="dispatch")
class TokenGraphQLView(TokenLoginRequiredMixin, GraphQLView):
authentication_classes = [TokenAuthentication]
class SessionGraphQLView(LoginRequiredMixin, GraphQLView):
pass
I'm trying to check permissions for some API requests.
I've already set auth users and auth_user_user_permissions and auth_permissions tables like view_company add_company bla bla, but the problem is not that. The problem is when I'm trying yo use decorator which
#permission_required('API.view_company', raise_exception=True)
it said to me
AttributeError: 'CompanyDetailView' object has no attribute 'user'
Most probably it's looking for the user because its gonna check user_permission is it available to view companies or not but my view which I declared in urls.py (path('companies//', CompanyDetailView.as_view()),) has not have user object that's why error message returned attribute error, how can I solve this, thanks a lot
I tried to set example user in view class, in the beginning, it worked because view was looking for user object, i can not use that way because every request has different user
import rest_framework
from rest_framework import status
from django.contrib.auth.models import User
from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth.decorators import permission_required
class CompanyDetailView(APIView):
#permission_required('api.view_company', raise_exception=True)
def get(self, request, id):
try:
request_data = {}
request_data['request_method'] = request.method
request_data['id'] = id
companies = Company.objects.get(id=id)
status = rest_framework.status.HTTP_200_OK
return Response(companies, status)
bla bla bla
url line was =
path('companies/<int:id>/', CompanyDetailView.as_view()),
my error message was : AttributeError: 'CompanyDetailView' object has no attribute 'user'
when i debug and i see request.user.has_perm('view_company')returned false but still api give responses, it suppose to say you are not allow to view companies
The mechanism of Django Views and Django Rest Framework Views are a bit different, that's why you've got that error message. permission_required will try to access user field of your view to check user permission using has_perm method. But APIView didn't have user field inside of it.
To get rid of this, you might want to use permissions which provided by Django Rest Framework to restrict the access.
But if you still want to use built-in permission of Django to restrict the access to your view, you could create a Permission class which will use has_perm to check user permission. Like so:
from rest_framework import permissions
from rest_framework import exceptions
class ViewCompanyPermission(permissions.BasePermission):
def has_permission(self, request, view):
if not request.user.has_perm('api.view_company'):
raise exceptions.PermissionDenied("Don't have permission")
return True
and use it on your view via permission_classes field:
class CompanyDetailView(APIView):
permission_classes = (ViewCompanyPermission, )
def get(self, request, id):
try:
request_data = {}
request_data['request_method'] = request.method
request_data['id'] = id
companies = Company.objects.get(id=id)
status = rest_framework.status.HTTP_200_OK
return Response(companies, status)
In case you want to replicas the permission_required behavior, you could do something like this:
from rest_framework import permissions
from rest_framework import exceptions
def permission_required(permission_name, raise_exception=False):
class PermissionRequired(permissions.BasePermission):
def has_permission(self, request, view):
if not request.user.has_perm(permission_name):
if raise_exception:
raise exceptions.PermissionDenied("Don't have permission")
return False
return True
return PermissionRequired
Then you can use it like:
class CompanyDetailView(APIView):
permission_classes = (permission_required("api.view_company", raise_exception=True), )
# ...
You can't easily use django permissions with django rest framework.
there is a tutorial about django-rest-framework permissions at:
https://www.django-rest-framework.org/api-guide/permissions/
Based on the description permission_required this decorator should be used for a function view, where first argument is request and you try to apply it for the class method where the first argument is self in your case instanse of the CompanyDetailView so you get the error. And you should use another way to check the permissions.
You can read some examples in here: decorators-on-django-class-based-views
I want to add some REST API views to an existing Django project, which uses vanilla Django views. For that I'd like to use the REST Framework. I wonder if I can I mix Django and RF views in a single project and what pitfalls this might have (e.g. with authentication).
Yes you can surely use both of them at the same time, there shouldn't be any issue. Typically the Django views use SessionAuthentication, adn you will use DRF using TokenAuthentication -- best practice is to add both Session and Token authentication to the authentication_classes in the DRF views - that way you can use the browsable api pages to browse the apis once you have signed in via password (session authentication) as well
class GenericViewTest(SuperuserRequiredMixin, View):
def get(self, request, *args, **kwargs):
return HttpResponse("Test")
class PostTrackingCode(CreateAPIView):
"""
"""
authentication_classes = (SessionAuthentication, TokenAuthentication) ----> note this
permission_classes = (permissions.IsAuthenticated,)
serializer_class = TrackingInfoWriteSerializer
model = TrackingInfo
I'm using django-allauth on my website for social logins. I also have a REST API powered by django-rest-framework that serves as the backend of a mobile app. Is there a way I can directly plug in allauth's authentication backend to the REST api so that I can validate (and register) users who use Facebook login in the mobile app?
To clarify: The Facebook login part is handled by native SDKs. I need an endpoint that works like POST /user (that is, creates a new user), but takes Facebook oauth token as input instead of email/password etc.
You can use Django Rest Auth for this which depends on django-allauth. It's very easy to integrate.
You can use this libray for social authentication django-rest-framework-social-oauth2. Try this django-allauth related code
urls.py
urlpatterns = [
url(
r'^rest/facebook-login/$',
csrf_exempt(RestFacebookLogin.as_view()),
name='rest-facebook-login'
),
]
serializers.py
class EverybodyCanAuthentication(SessionAuthentication):
def authenticate(self, request):
return None
views.py
class RestFacebookLogin(APIView):
"""
Login or register a user based on an authentication token coming
from Facebook.
Returns user data including session id.
"""
# this is a public api!!!
permission_classes = (AllowAny,)
authentication_classes = (EverybodyCanAuthentication,)
def dispatch(self, *args, **kwargs):
return super(RestFacebookLogin, self).dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs):
try:
original_request = request._request
auth_token = request.GET.get('auth_token', '')
# Find the token matching the passed Auth token
app = SocialApp.objects.get(provider='facebook')
fb_auth_token = SocialToken(app=app, token=auth_token)
# check token against facebook
login = fb_complete_login(original_request, app, fb_auth_token)
login.token = fb_auth_token
login.state = SocialLogin.state_from_request(original_request)
# add or update the user into users table
complete_social_login(original_request, login)
# Create or fetch the session id for this user
token, _ = Token.objects.get_or_create(user=original_request.user)
# if we get here we've succeeded
data = {
'username': original_request.user.username,
'objectId': original_request.user.pk,
'firstName': original_request.user.first_name,
'lastName': original_request.user.last_name,
'sessionToken': token.key,
'email': original_request.user.email,
}
return Response(
status=200,
data=data
)
except:
return Response(status=401, data={
'detail': 'Bad Access Token',
})
While I'm not quite sure how to use allauth and rest-fremework together, allauth does not offer such an endpoint.
Suggestion: make your own that does a variation of the following:
Call allauth.socialaccount.providers.facebook.views.fb_complete_login(None, socialtoken) where socialtoken is as created in login_by_token. That performs (a few functions deeper) a django.contrib.auth.login, possibly creating the acct.
After that, for use on mobile devices, it might be possible to the the auth (not FB) token: get the user data (from session?), and call rest_framework.authtoken.views.obtain_auth_token
Notes:
1. This offers no way to resolve email conflicts or connect social/local accts.
2. I haven't tried it - please post code if you can get it working.
You could use djoser but I don't know how it cooperates with allauth:
https://github.com/sunscrapers/djoser