I am following instructions from here How can I login to django using tastypie to create a UserResource that can be used to login to my django user.
However, I am running into HTTP 500 error when I run the code. I tried to debug it myself, but could not figure it out. I am not sure how I can troubleshoot the 500 error. Any thought you can give is appreciated.
Thanks!!
My code is as below:
#####api.py
from registration.views import register
from tastypie.resources import ModelResource
from tastypie.constants import ALL
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
from tastypie.http import HttpUnauthorized, HttpForbidden
from django.conf.urls.defaults import url
from tastypie.utils import trailing_slash
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
fields = ['first_name', 'last_name', 'email']
allowed_methods = ['get', 'post']
resource_name = 'user'
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/login%s$" %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('login'), name="api_login"),
url(r'^(?P<resource_name>%s)/logout%s$' %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('logout'), name='api_logout'),
]
def login(self, request, **kwargs):
self.method_check(request, allowed=['post'])
print "reached login auth"
data = self.deserialize(request, request.raw_post_data, format=request.META.get('CONTENT_TYPE', 'application/json'))
username = data.get('username', '')
password = data.get('password', '')
print "reached login auth"
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request, user)
return self.create_response(request, {
'success': True
})
else:
return self.create_response(request, {
'success': False,
'reason': 'disabled',
}, HttpForbidden )
else:
return self.create_response(request, {
'success': False,
'reason': 'incorrect',
}, HttpUnauthorized )
def logout(self, request, **kwargs):
self.method_check(request, allowed=['get'])
if request.user and request.user.is_authenticated():
logout(request)
return self.create_response(request, { 'success': True })
else:
return self.create_response(request, { 'success': False }, HttpUnauthorized)
########test_login.py
import requests
import json
from urllib2 import urlopen
import datetime
import simplejson
url = 'http://127.0.0.1:8000/api/user/login'
data = {'username' :'sv3#gmail.com', 'password' : 'pass'}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
print json.dumps(data)
r = requests.post(url, data=json.dumps(data), headers=headers)
print r
#####urls.py
from userdetails.api import UserResource
user_resource = UserResource()
urlpatterns = patterns('',
......
(r'^api/', include(user_resource.urls)),
)
When deploying via WSGI using print statement can raise IOErrors. Try to comment them out or redirect the output.
In Django, how do I allow print statements to work with Apache WSGI?
http://blog.dscpl.com.au/2009/04/wsgi-and-printing-to-standard-output.html
Append a slash
url = 'http://127.0.0.1:8000/api/user/login/'
Related
This project was working before, but then Heroku took away free tier so I have been trying to deploy somewhere else, but now all of a sudden I cannot even create a user locally even though I could before... Now when I create a user I get the error mentioned in the title.
serializers folder
common.py file
from xml.dom import ValidationErr
from rest_framework import serializers
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.hashers import make_password
User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
confirm_password = serializers.CharField(write_only=True)
def validate(self, data):
password = data.pop('password')
confirm_password = data.pop('confirm_password')
if password != confirm_password:
raise ValidationErr({ 'confirm_password': 'Does not match the password'})
password_validation.validate_password(password)
data['password'] = make_password(password)
return data
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', 'confirm_password')
views.py file
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import PermissionDenied
from django.contrib.auth import get_user_model
import jwt
User = get_user_model()
from datetime import datetime, timedelta
from jwt_auth.serializers.common import UserSerializer
from django.conf import settings
class RegisterView(APIView):
def post (self, request):
create_user = UserSerializer(data=request.data)
try:
create_user.is_valid(True)
create_user.save()
return Response(create_user.data, status=status.HTTP_201_CREATED)
except Exception as e:
print(str(e))
return Response(e.__dict__ if e.__dict__ else str(e), status=status.HTTP_422_UNPROCESSABLE_ENTITY)
class LoginView(APIView):
def post(self, request):
password = request.data.get('password')
username = request.data.get('username')
try:
user_login = User.objects.get(username=username)
except User.DoesNotExist:
raise PermissionDenied('Credentials are incorrect!')
if not user_login.check_password(password):
raise PermissionDenied('Credentials are incorrect!')
dt = datetime.now() + timedelta(days=7)
token = jwt.encode(
{
'sub': user_login.id,
'exp': int(dt.strftime('%s'))
},
settings.SECRET_KEY,
'HS256'
)
print('TOKEN ----->', token)
return Response({ 'token': token, 'message': f'Welcome back {user_login.username}' })
rest-framework branch "3.9.x" (github)
def is_valid(self, raise_exception=False):
...
rest-framework branch "master" (github)
def is_valid(self, *, raise_exception=False):
...
so "master" now: is_valid() only accepts keyword argument "raise_exception=True":
class RegisterView(APIView):
def post (self, request):
create_user = UserSerializer(data=request.data)
try:
create_user.is_valid(raise_exception=True)
....
Change it into something like this:
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
return Response(user.data, status=status.HTTP_201_CREATED)
the rest of your code...
#action(detail=True, methods=['get'], url_path='password-reset/<uid64>/<token>', url_name='password-reset-confirm')
def password_reset(self, request, uid64, token):
pass
this is the url ( http://localhost:8000/user/2/password-reset/Mg/az44bk-48c221372ceaca98b4090a421131d5f3 ) I am trying to reach, but it keeps returning 404 page not found
Update::
urls file:
from django.urls import path
from django.urls.conf import include
from .views import UserViewset, CompanyViewset, ProfileViewset
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('user', UserViewset, basename='user')
router.register('company', CompanyViewset, basename='company')
router.register('profile', ProfileViewset, basename='profile')
urlpatterns = [
path('', include(router.urls)),
path('<int:id>', include(router.urls)),
]
views file:
#action(detail=True, methods=['post'], url_path='request-reset-email', url_name='request-reset-email')
def request_reset_password_email(self, request, pk):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.data.get('email')
user = self.get_object()
if email == user.email:
token = PasswordResetTokenGenerator().make_token(user)
current_site = get_current_site(request=request).domain
relativeLink = '/user/' + str(user.id) + '/password-reset/' + token
absurl = 'http://'+current_site + relativeLink
email_body = 'Hello \n Use link below to reset your password \n' + absurl
subject = 'Reset your password'
send_mail(subject, email_body, EMAIL_HOST_USER, [
user.email], fail_silently=False)
return Response({'success': 'we have sent you an email'}, status=status.HTTP_200_OK)
return Response({'failed': 'email does not match'}, status=status.HTTP_404_NOT_FOUND)
#action(detail=True, methods=['patch'], url_path=r'password-reset/(?P<token>\w+)', url_name='password-reset-confirm')
def set_new_password(self, request, pk, token):
user = self.get_object()
if not PasswordResetTokenGenerator().check_token(user, 'az569s-6ad4e11c2f56aa496128bc1c923486cb'):
return Response({'error': 'Token is not valid'}, status=status.HTTP_401_UNAUTHORIZED)
serializer = self.get_serializer(data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
password = serializer.data.get('password')
user.set_password(password)
user.save()
return Response({'success': 'success'})
serializers
class ResetPasswordEmailRequestSerializer(serializers.Serializer):
email = serializers.EmailField()
class SetNewPasswordSerializer(serializers.Serializer):
password = serializers.CharField(min_length=6, max_length=20)
Recently I have removed the uid64 since I am not using it, tested everything and it does work when hardcode the token in the url
Try this:
#action(
detail=True,
methods=['get'],
url_path=r'password-reset/(?P<uid64>\w+)/(?P<token>\w+)',
url_name='password-reset-confirm'
)
def password_reset(self, request, pk, uid64, token):
pass
You will have to capture the three parameters: pk, uid64, and token
Whenever I try to make a POST request, the url automatically redirects to the login resulting in BAD_REQUEST
This is my code for login:
class LoginView(View):
def get(self, *args, **kwargs):
form = LoginForm()
context = {
'form':form
}
return render(self.request, 'core/login.html', context)
def post(self, *args, **kwargs):
form = LoginForm(self.request.POST or None)
if form.is_valid():
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(self.request, email=email, password=password)
if user is None:
messages.add_message(self.request, messages.ERROR, 'Enter valid email or password')
return redirect('login')
login(self.request, user=user)
return redirect('home')
return redirect('login')
This is my post function for api:
class NoteCreateView(LoginRequiredMixin, APIView):
def post(self, *args, **kwargs):
print(self.request.user)
data = (self.request.data)
data['id'] = self.request.user.id
print(data)
serializer = NoteSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.data, status=HTTP_400_BAD_REQUEST)
And this is the POST request made from frontend
e.preventDefault()
const note = document.getElementById('add-note').value
let date = new Date()
const data = {
'note':note,
'date_created':date
}
let url = 'http://localhost:8000/api/notes/create/'
fetch(url, {
method:'POST',
headers:{
'content-type':'application/json',
'X-CSRFToken':csrftoken
},
body:JSON.stringify(data)
})
.then(res => {
getAllNotes()
document.getElementById('form').reset()
})
})
The url that it always redirects on making POST request
/login/?next=/api/notes/create/
urls.py
api:
urlpatterns = [
path('notes/', NoteListView.as_view(), name='all'),
path('notes/create/', NoteCreateView.as_view(), name='create')
]
frontend
urlpatterns = [
path('register/', RegisterView.as_view(), name='register'),
path('login/', LoginView.as_view(), name='login'),
path('', HomeView.as_view(), name='home'),
path('logout/', LogoutView.as_view(), name='logout')
]
settings.py
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'
You should make use of authentication_classes and permission_classes of the Django Rest Framework. Check it out here: https://www.django-rest-framework.org/api-guide/authentication/
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
Plus I can highly recommend you the to use a JWT token with following package: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/
Like that you can get rid of your Login View:
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
...
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
...
]
I am trying to create a sign up view that can both register the user model according to the POST data, and that includes in the response both:
The created user data (i.e. {'username': 'foo.bar', 'email': 'test#test.io'})
The token data (i.e. {'expires_in': 36000, 'access_token': 'foo', 'scope': 'read write', 'token_type': 'Bearer', 'refresh_token': 'bar'})
So they don't have to login after signing up. I am trying to do so with this views.py:
import json
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
import requests
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView
from accounts.serializers import UserSerializer, UserProfileSerializer
class UserProfile(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, format=None):
return Response()
def post(self, request, format=None):
user_profile = UserProfileSerializer(data=request.data) # data
if user_profile.is_valid():
user_profile.save() # returns User instance
# ask for a token
r = requests.post('http://myapp.com/auth/token',
data = {'client_id': '',
'client_secret': '',
'grant_type': 'password',
'username': user_profile.user.username,
'password': request.data['password']})
response_dict = json.loads(r.text)
response_dict['user_profile'] = user_profile.data
return Response(response_dict, status=status.HTTP_201_CREATED)
else:
return Response(user_profile._errors, status=status.HTTP_400_BAD_REQUEST)
however I don't know how am I supposed to get my provider's client_id and client_secret (I created my oauth2_provider application).
Am I supposed to get it as in my_app = oauth2_provider.models.Application.objects.all()[0] and then get them as my_app.client_id and my_app.client_secret? Is there a nicer way to do so?
Plus, has this any security flaws?
Thank you!
I am currently working on a project which requires Api, i choose Tastypie and implemented the following. I want to use tastypie's default features like ApiKeyAuthentication, DjangoAuthorization etc... in my code
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()`enter code here`
fields = ['first_name', 'last_name', 'email']
allowed_methods = ['get', 'post']
resource_name = 'user'
def override_urls(self):
return [
url(r"^(?P<resource_name>%s)/login%s$" %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('login'), name="api_login"),
url(r'^(?P<resource_name>%s)/logout%s$' %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('logout'), name='api_logout'),
]
def login(self, request, **kwargs):
self.method_check(request, allowed=['post'])
data = self.deserialize(request, request.raw_post_data,format=request.META.get('CONTENT_TYPE', 'application/json'))
username = data.get('username', '')
password = data.get('password', '')
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request, user)
return self.create_response(request, {
'success': True
})
else:
return self.create_response(request, {
'success': False,
'reason': 'disabled',
}, HttpForbidden )
else:
return self.create_response(request, {
'success': False,
'reason': 'incorrect',
}, HttpUnauthorized )
def logout(self, request, **kwargs):
self.method_check(request, allowed=['get'])
if request.user and request.user.is_authenticated():
logout(request)
return self.create_response(request, { 'success': True })
else:
return self.create_response(request, { 'success': False }, HttpUnauthorized)
Use Code Below
from django.contrib.auth.models import User
from tastypie.authentication import ApiKeyAuthentication
from tastypie.authorization import DjangoAuthorization
from tastypie.resources import ModelResource
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'auth/user'
excludes = ['email', 'password', 'is_superuser']
# Add it here.
authentication = ApiKeyAuthentication()
authorization = DjangoAuthorization()