I would like to ask how I can do authentication and query tags limited to a specific user (owner) using Graphen Relay and Django JWT.
Here is my Model:
class Tag(models.Model):
"""Tag to be used for a objective"""
name = models.CharField(max_length=255)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='tag',
on_delete=models.CASCADE,
)
Here is my Schema code:
class TagNode(DjangoObjectType):
class Meta:
model = Tag
filter_fields = ['name']
interfaces = (relay.Node,)
Here is my Query:
class Query(graphene.ObjectType):
tag = relay.Node.Field(TagNode)
all_tags = DjangoFilterConnectionField(TagNode)
def resolve_all_tags(self, info):
# context will reference to the Django request
print(info.context.user)
if not info.context.user.is_authenticated:
return Tag.objects.none()
else:
return Tag.objects.filter(user=info.context.user)
Here is the document for Django JWT: https://github.com/flavors/django-graphql-jwt but I do not know how to get the user (already use info.context.user) but it does not work,
when I tried to add Authorization (JWT token) in the header or as an argument. It also does not work.
Basically you are doing everything correctly and your user should be fetched successfully.
I've just checked and it works in my case, I use JWT like a cookie but it doesn't matter.
Had you authorized your user when tried to get tags?
Because regarding JWT you need to authorize your user and receive the JWT token. Only after that the real user will be presented in the request when you call the tags query. Otherwise the user will be anonymous.
Related
I'm trying to update my user model by adding a profile picture. I'm stuck on the fact that DRF requires a lookup for PUT requests. But the client doesn't know the user pk they just have a login token. The user would normally be obtained in a view using request.user
views.py:
class UploadViewset(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = serializers.ImageUploadSerializer
parser_classes = [MultiPartParser]
models.py
class User(AbstractUser):
profile_image = models.ImageField(upload_to='testuploads/', null=True)
serializers.py
class ImageUploadSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'profile_image',
)
Posting give the following
"detail": "Method \"PUT\" not allowed.",
Which is caused by the fact that there is no individual resource identifier in the URL. Of course, the resource (a user) could be obtained from the request because it is an authenticated request.
How can I update the user model using DRF?
Update
urls.py
router.register(r'upload', views.UploadViewset)
I'm posting to endpoin upload/
I am developing a system where a user logs in and they can use the features of our service. We have used Django Authentication to make the User and Login backend.
This is the custom User model in my models.py -
class UserDef(AbstractUser):
def __str__(self):
return self.first_name + self.last_name
employeeId = models.IntegerField(unique=True)
email_address = models.EmailField()
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
USER_TYPES = [('1',"Admin"), ('2',"Director"), ('3',"Sr. Manager"), ('4',"Manager") , ('5',"Sr. Engineer"), ('6',"Assistant Regional Manager"), ('7',"Assistant to the Regional Manager")]
user_type = models.CharField(max_length=2048, choices=USER_TYPES)
created_at = models.DateTimeField(auto_now_add=True)
This in itself works really well and I was able to access all data as needed. I am writing my front-end in React though. Which means there's a certain disconnect between the templates html file and the actual React.js code. Which means I can't simply send the employeeId or some other attribute as context. Does anyone know how I can extract that User information from the Django DB (which we have connected to an RDS-MySQL server) and display it in my React app.
If the user is already logged in and a sessionID has been generated. You could just create an endpoint through where you can fetch the current user and then make a call to the endpoint in your React frontend.
#api_view(['GET'])
def current_user(request):
user = request.user
return Response({
'username' : user.username,
'firstname' : user.first_name,
'employeeID' : user.employeeId,
# and so on...
})
You could obviously, always use a serializer to ease the process.
here is my user model.
class User (models.Model):
username = models.CharField(max_length=50)
# token = models.CharField(max_length=50)
email_id = models.EmailField(max_length=50)
password = models.CharField(max_length=50)
is_deleted = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
updated_at = models.DateTimeField(auto_now_add=True, blank=True)
and here is my views for creating user
class UserView(APIView):
def post(self,request):
try:
# RequestOverwrite().overWrite(request, {'token':'string'})
user_data = UserDetailSerializer(data=request.data)
if not(user_data.is_valid()):
return Response(user_data.errors)
user_data.save()
return Response("user created successfully",status=status.HTTP_201_CREATED)
except Exception as err:
print(err)
return Response("Error while creating user")
now what i want to do is to create a token when i post a user and that token is used later for login.
also i want to validate user if it exist in database then make user authenticate.
what should i do..?any suggestion
below is my serializers.py
class UserDetailSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','username','email_id','password','is_deleted','created_at','updated_at')
extra_kwargs = {
'password': {
'required':True,
'error_messages':{
'required':"Please fill this field",
}
}
}
First and foremost, it seems you are defining a custom user that is not in any way connected the Django auth user. It is a very very bad idea and will be difficult to get it work with most Django features. You can check out how to customize the existing user if you really need to.
As for user authentication, using the DRF Token Authentication, the flow is this way:
Client sends request to create user
If user is created successfully, it requests for an authentication token using the user's login and password
The backend verifies user's credentials and issues a token
Client makes subsequent requests with the token
If token expires or user logs out, repeat 2-4
Check out how to do these in DRF's TokenAuthentication documentation.
Your question is not very specific so I'm not sure what sort answer you are expecting but following these steps should get you going.
Token creation should be realized in model User. You can set default value to token field.
Add parameter default to User model token field:
token = models.CharField(default=tokenGenerator, max_length=50)
tokenGenerator should be some function that returns some generated token.
Secondly UserDetailSerializer should be edited to get token if it's necessary but not required.
class UserDetailSerializer(serializers.ModelSerializer):
# You can also provide this default function like in model field
# for this token field
# default=tokenGenerator
token = serializers.CharField(max_length=50, required=False)
class Meta:
model = User
fields = (..., 'token')
extra_kwargs = ...
Now your model gets generated token for newly created User.
User should be auto logged in after registration but it's not that simple.
You need to specify how do you want to communicate with API. You want token so i guess that front should request api and token should be used from cookies.
If you send request from browser eg. jQuery you need to get response with generated token and save it in cookies. Your post response in DRF:
return Response({'token': user_data.get('token')},status=status.HTTP_201_CREATED)
And now you have some more steps:
Attach token to every browser request which requests API
Create authentication class to validate every request checks for correct token
...
Now, if you read this you realize that it needs some work. If you know that and you intentionally want to create this, you need to create mentioned view authentication class.
Else try to use libs like oauth for django. That will provide you ready classes for token management.
Also as #Ken4scholars mentioned, try to use Django User model. It have good methods for validation etc. without make this done manually.
I'm writing an application with django-tastypie and following are my models.py and resource.py files.
Models.py:
import uuid
from django.db import models
class User(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=50, null=False)
email = models.EmailField(max_length=254, null=False)
password = models.CharField(max_length=100, null=False)
role = models.CharField(max_length=16, default='basic', null=False)
def __unicode__(self):
return self.name, self.email
Resources.py:
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
from api.models import User
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
authorization = Authorization()
excludes = ['password']
#allowed_methods = ['get']
Now the thing is that whenever I hit an API end point from postman, the user is created directly. Now what I don't understand is that whether the request data goes into resources and then into database or directly into the database? Actually, the thing is that I need to apply some changes to the data before it is stored in the database, like hashing the password and then storing the object in the database. I'm new to django, so how can I achieve that?
Like in Flask, we can do something like:
#user.route('/users', methods=['POST'])
def create_user(user_id):
data = request.get_json(force=True)
# do all the changes we want
user = User(data)
db.session.add(user)
db.session.commit()
Now if any request comes at '/users' endpoint, we can get it's data in the 'data' variable and then whatever changes we want before storing in the database. But how to do that in django with tastypie.
Any help would be appreciated
If you have to massage data before entering into database then Tastypie has the notion of hydrate and dehydrate methods.
Check that. Here is reference hydrate and dehydrate
In every web framework the data that sent with the request passed to the api endpoint through some mechanism and the same thing happens in Tastypie (you can read about it in Tastypie documentation under Flow Through The Request/Response Cycle).
If you want to change the data that you sending/receiving read about Hydrate/Dehydrate, in your case you want to use dehydrate on user password but I recommend you to save the effort and instead use custom user model by inheriting from AbstractUser, that way you can get hashed password by default when User object is saved to your DB.
I am using tastypie to create a RESTful API. I have hit a snag with limiting user authorization based on the django admin permissions. Per the docs, I am trying to implement DjangoAuthorization().
class myResource(ModelResource):
class Meta:
queryset = myModel.objects().all()
allowed_methods = ['get','post']
authentication = ApiKeyAuthentication()
authorization = DjangoAuthorization()
Currently, a user fakeuser with no Django permissions at all on myModel can still GET data from the api. This user is appropriatly restricted from POSTing data.
tl;dr How can I extend the DjangoAuthorization() class to restrict GET for users with no Django permisions on a model
Write your own Authorization Backend extending from DjangoAuthorization which overrides access methods according to your criteria, one example of how to override the read_detail (GET) method below:
from tastypie.authorization import DjangoAuthorization
from tastypie.exceptions import Unauthorized
class CustomDjangoAuthorization(DjangoAuthorization):
def read_detail(self, object_list, bundle):
result = super(CustomDjangoAuthorization, self).read_detail(object_list, bundle)
# now we check here for specific permission
if not bundle.request.user.has_perm('any_permission'):
raise Unauthorized("You are not allowed to access that resource.")
return result
Now use CustomDjangoAuthorization class in your resource:
class myResource(ModelResource):
class Meta:
queryset = myModel.objects().all()
allowed_methods = ['get','post']
authentication = ApiKeyAuthentication()
authorization = CustomDjangoAuthorization()