apragacz / django-rest-registration Goto Github PK
View Code? Open in Web Editor NEWUser-related REST API based on the awesome Django REST Framework
Home Page: https://django-rest-registration.readthedocs.io/
License: MIT License
User-related REST API based on the awesome Django REST Framework
Home Page: https://django-rest-registration.readthedocs.io/
License: MIT License
Feature request mentioned in issue #2.
Currently, only register and verify_registration views support signals.
I am trying to use username instead of user_id. But i am getting this error
File "/home/krishna/Projects/django/cms/account/views.py", line 79, in _calculate_salt if registration_settings.REGISTER_VERIFICATION_ONE_TIME_USE: File "/home/krishna/anaconda3/envs/django/lib/python3.7/site-packages/rest_registration/utils/nested_settings.py", line 38, in __getattr__ self=self, attr=attr)) AttributeError: Invalid REST_REGISTRATION setting: 'REGISTER_VERIFICATION_ONE_TIME_USE'
However this is not related to username or userid data but yet i am getting this error. I tried to trackdown this error in nested_settings.py and settings_field.py but there is no problem.
Also i am using whole view.py from
rest-registration/api/views/register.py
I want to create a registration form without password_confirm field. Is there a way to change the registration api accordingly?
Using the default settings, there doesn't seem to be a way for a user to recover from an initial expired verification link. To reproduce:
REGISTER_VERIFICATION_PERIOD
to endThe signature has expired, as expected. From here, the user should either be able to re-register or to use the send-password-reset-link
in order to complete verificaiton. If they try to register, they will get a "User with this email already exists" error, and if they try to reset the password they will get a "User not found" error.
My suggested fix would be to pass the required_verify=False
option to the get_user_by_lookup_dict
method here: https://github.com/apragacz/django-rest-registration/blob/master/rest_registration/api/views/reset_password.py#L73
Here you have server where it's deployed and example request:
curl -X POST -d '{"username": "anyone", "password":"pass1234", "password_confirm":"pass1234"}' --header 'Content-Type: application/json' --header 'Accept: application/json' 'http://luken-qa.herokuapp.com/api/v1/accounts/register/'
my settings looks like this:
REST_REGISTRATION = {
'REGISTER_VERIFICATION_ENABLED': True,
"REGISTER_VERIFICATION_URL": "/verify-account/",
'RESET_PASSWORD_VERIFICATION_URL': '/reset-password/',
'REGISTER_EMAIL_VERIFICATION_ENABLED': True,
'VERIFICATION_FROM_EMAIL': '[email protected]',
"REGISTER_EMAIL_VERIFICATION_URL": "/verify-email/",
}
Is this expected?
The current send_verification
function only allows sending of plain text emails, would be nice to have and option in the settings to send html emails.
I believe sending of html emails can be done by email_msg.content_subtype = 'html'
django-rest-registration/rest_registration/notifications/email.py
Lines 8 to 29 in 0c1f599
Additional task for #22
Right now we can change the frontend URL in settings to create a link like this:
http://front-host/confirm-email/?user_id=4×tamp=1553208302&signature=0uK-akKnv9kSHNlPI1BDhT8Ddsbc
.
Is it possible to change the parameters to something like this?:
http://front-host/confirm-email/4/1553208302/0uK-akKnv9dkSHNlPI1BDhT8Dsbc
Create optional Django views redirecting after verification to success / error page. This was mentioned in issue #11.
Is it possible to customize the email registration? Especially add some more validation on the new email. I want to check if the email is already used by another user before allowing it to be set as the email address of the user.
I already overrode the serializer used in the registration process to enforce unique email addresses and I would like to know if we can have the same for the register email.
If one is using AllowAllUsersModelBackend
to display a different error for inactive users, these users will get a successful message for login, but their session won't work.
The fix is just adding
if not user.is_active:
raise BadRequest('This account is inactive.')
to the login view.
Allow send reset password link serializer to be replaceable.
If the client does not get an AuthToken after registration, the user must provide credentials immediately after registering.
I was able to add a token to the default serializer with the following custom serializer, but the token should really be created in the view instead.
from rest_registration.api.serializers import DefaultUserProfileSerializer
from rest_framework import serializers
from rest_framework.authtoken.models import Token
class MyRegistrationSerializer(DefaultUserProfileSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.Meta.fields += ('token',)
token = serializers.SerializerMethodField()
def get_token(self, user):
token = Token.objects.create(user=user)
return token.key
and in settings.py
:
REST_REGISTRATION = {
# ...
'REGISTER_OUTPUT_SERIALIZER_CLASS': '...MyRegistrationSerializer',
}
I see that we can use token authentication method but there is no documentation on how to switch to token authentication from session authentication.
My urls verification emails are look like this:
Please verify your account by clicking on this link:
http://localhost:4200/verify-user/?user_id=8×tamp=1535128294&signature=Ls68DclJF8PlP7pfLOFc3VsvSXA
If I try to post without the email field I am told that email is required.
If i manually put in the email then it tells me invalid signature
I also noticed that your test doesn't check for the presence of an email field
From issue #38:
I will recommend using the email instead of the username to find the user to whom the reset password link is sent. That way only the user (and anyone who knows his email address) can request the reset password link to be sent.
I got an error after I register with:
username:_____
first_name:____
last_name: _____
email:__________
password:______
conf password:________
How Can I fix it?
Request Method: | POST |
---|---|
http://127.0.0.1:8000/accounts/register/ | |
2.2.1 | |
ConnectionRefusedError | |
[Errno 61] Connection refused | |
/Users/vannak/miniconda3/lib/python3.7/socket.py in create_connection, line 716 | |
/Users/vannak/.local/share/virtualenvs/vannak-9C9zOB6z/bin/python | |
3.7.0 | |
['/Users/vannak/Desktop/authentication', '/Users/vannak/.local/share/virtualenvs/vannak-9C9zOB6z/lib/python37.zip', '/Users/vannak/.local/share/virtualenvs/vannak-9C9zOB6z/lib/python3.7', '/Users/vannak/.local/share/virtualenvs/vannak-9C9zOB6z/lib/python3.7/lib-dynload', '/Users/vannak/miniconda3/lib/python3.7', '/Users/vannak/.local/share/virtualenvs/vannak-9C9zOB6z/lib/python3.7/site-packages'] | |
Mon, 3 Jun 2019 15:41:53 +0000 |
Because of the issue #34, we need to allow unverified users to be able to reset the password. By default we are using the is_active
user attribute to hold the information whether the user is verified or not.
If someone is using this field to disable (block) the users, the user could easily unblock himself by using the "reset password" endpoint.
Currently I assume we must implement the view that we specify in the REGISTER_EMAIL_VERIFICATION_URL value. Because I only found the implementation of the POST version of it (verify_email in register_email.py) and the link send to the email is a get of course. Is this right or its already implemented?
Hey there!
What would be the best way to make additional fields required for the user model? For example, it appears that only username and password are required in Django User model, but I would like to have first_name
, last_name
, and email
required via the "accounts/register" endpoint.
One way I could do this would be to set define a custom Django user model, but I think I want first_name
and last_name
to not be required internally in case I want to make daemon/service accounts.
I think the next best way would be to override the registration serializer via the DefaultRegisterUserSerializer
setting. Is this what you would recommend? Could you write a quick example using this method if so?
PS: Thanks for making this library, @apragacz! I'm excited to finish implementing it. It looks like exactly what I need. :)
It seems to me that, returning profile serializer after registration is not appropriate.
I think registration should have it's own serializer to return data.
For example when user registers I'm creating a bunch of staff in profile that user should not know about before it activates account and logs in, but now he will receive all that back when first time hitting my registration endpoint, and there is no way to reconfigure it, because I do need profile serializer in a way it implemented now.
I would expect this behavior more after login
than register
.
This should be done after #48 will be done.
From #48:
I guess the initial solution could be leaving the name user_id but allow the user identification field to be changed, and then make the change user_id -> user in next major version (0.5.0?, 1.0.0?). Probably user_id should be deprecated first, and then removed altogether.
IMPORTANT: please remember to provide backward compatibility for the deprecated user_id
in the verification views (log a deprecation warning when user_id
is used?), then remove it altogether in next major version.
For now, login API is taking two fields i.e. "login" and "password" for logging in a user. How can we make this API compatible with the email address of the user? i.e. a user can log in to the website by entering his username or email address along with the password.
So I hit resgiter email and receive a following email:
Please verify your account by clicking on this link:
http://localhost:8000/verify-account/?user_id=cb185c07-143f-4d99-ac64-18a280e221ff×tamp=1519787713&signature=4uB1gS9O32bPa7skbCvEh88XCec
I want to activate this account, so I take user_id, timestamp signature and email address end send this to our POST /api/v1/accounts/verify-email/
I get response:
{
"detail": "Invalid signature"
}
And btw,
urls that i get in email is not working
Additional task for #22
Currently, the user has to log in with his credentials, but this is pointless from a security point of view, since if he has access to the email he could reset the password.
There should be an option to automatically log in users after they verify their account.
There doesn't seem to be any checks for whether the email used to register is unique, anyway we can fix that?
Just curious why did you implement this get_user_model_class
in utils.py
?
https://github.com/szopu/django-rest-registration/blob/master/rest_registration/utils.py#L10 ?
When django has the same function available out of the box?
https://docs.djangoproject.com/en/2.0/topics/auth/customizing/#referencing-the-user-model
Add additonal verification to avoid spamming the user with unnecessary e-mails.
For the https://frontend-url/reset-password/
I receive a url like this:
http://localhost:8000/c/eJwVj81uhCAUhZ9Gd5J7-RFYuBg7cdE-RIOCIxkRBrU2ffoyydl9J_nOsZ0wxjFW-44CKuDYImMaWoJEKaTIea9R9hI1VhzCg6zRbH_Ex3rpQClhlAI6GQvSKsUch9GNOFrJOZX12i3HkSp2q-hQsp5PtzUpR0sWl-PzNCmRKYaCstvd0SSz71fM9l1nw7m7_O1txe6WUzPPGpq2xanh2kFTFKYRI6NgteazaCvaHj64_TChGO8oKCvzNBcF7P6xmePMrgAVR7A_-vdcaP85fcivS76uFxtg4HXuUlz9FgMClLePYPz6HvgPYxNXhA
How do I extract timestamp and signature form it to be able to send to:
POST /api/v1/accounts/reset-password/
?
Token is not deleted and still available for use after receiving 'Logout successful'
from logout view.
Can overwrite LoginSerializer
using setting in same way like we can redefine registration and profile serializers?
'LOGIN_SERIALIZER_CLASS': 'rest_registration.api.serializers.DefaultLoginUserSerializer',
For example django-rest-auth
has this: TOKEN_SERIALIZER
setting.
http://django-rest-auth.readthedocs.io/en/latest/configuration.html
Shouldn't the link that gets sent to activate an account only work once? Seems especially relevant when having 'REGISTER_VERIFICATION_AUTO_LOGIN': True
So I did setup everything like we you have here in example, and it seems like django-rest-swagger
does not pick endpoints.
Here are my urls:
from django.conf import settings
from django.urls import path, re_path, include, reverse_lazy
from django.conf.urls.static import static
from django.contrib import admin
from django.views.generic.base import RedirectView
from rest_framework.routers import DefaultRouter
from rest_framework.authtoken import views
from .users.views import UserViewSet, UserCreateViewSet
from rest_framework_swagger.views import get_swagger_view
schema_view = get_swagger_view(title='Luken API')
router = DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'users', UserCreateViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('rest_registration.api.urls')),
path('api/v1/', include(router.urls)),
path('api/swagger/', schema_view),
re_path(r'^$', RedirectView.as_view(url=reverse_lazy('api-root'), permanent=False)),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Here is the result.
If you notice accounts are not being displayed.
The following command generates the attached error log
$ python run_tests.py
'''
Creating test database for alias 'default'...
SystemCheckError: System check identified some issues:
ERRORS:
?: (rest_registration.E001) RESET_PASSWORD_VERIFICATION_URL is not set
?: (rest_registration.E002) register verification is enabled, but REGISTER_VERIFICATION_URL is not set
?: (rest_registration.E003) register email verification is enabled, but REGISTER_EMAIL_VERIFICATION_URL is not set
?: (rest_registration.E004) VERIFICATION_FROM_EMAIL is not set
'''
Hi @apragacz
First of all, thanks for your work on this nice package. It has minimum set of features and it's just exactly what I need for one of the projects I'm working on.
Now, back to the issue, this package is not compatible with django-rest-framework-simplejwt package which is recommended by django-rest-framework official docs as implementation of JWT authentication.
Problem may also appear with anything that uses django models and specified in rest framework settings (e.g. custom authenticating classes, custom permission classes, etc.). Issue is caused by this line in code triggering all django rest framework settings import while doing checks.
Below is full traceback that appears when I do runserver with following packages installed:
Django==2.1.7
django-rest-registration==0.3.14
djangorestframework==3.9.1
djangorestframework-simplejwt==4.0.0
pkg-resources==0.0.0
PyJWT==1.7.1
pytz==2018.9
and following django settings (others have default values)
INSTALLED_APPS = [
'django.contrib.auth',
'rest_registration',
'rest_framework',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
(venv) a-shestakov@ubuntu:~/Projects/test_project/jwt_test$ ./manage.py runserver
Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0x7f372c9a7510>
Traceback (most recent call last):
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/utils/autoreload.py", line 225, in wrapper
fn(*args, **kwargs)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 109, in inner_run
autoreload.raise_last_exception()
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/utils/autoreload.py", line 248, in raise_last_exception
raise _exception[1]
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/core/management/__init__.py", line 337, in execute
autoreload.check_errors(django.setup)()
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/utils/autoreload.py", line 225, in wrapper
fn(*args, **kwargs)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/apps/registry.py", line 89, in populate
app_config = AppConfig.create(entry)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/apps/config.py", line 116, in create
mod = import_module(mod_path)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_registration/apps.py", line 3, in <module>
import rest_registration.checks # noqa
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_registration/checks.py", line 7, in <module>
from rest_registration.notifications.email import parse_template_config
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_registration/notifications/__init__.py", line 1, in <module>
from .email import ( # noqa: F401
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_registration/notifications/email.py", line 10, in <module>
from rest_registration.utils.users import get_user_setting
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_registration/utils/users.py", line 4, in <module>
from rest_framework.generics import get_object_or_404
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework/generics.py", line 11, in <module>
from rest_framework import mixins, views
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework/views.py", line 19, in <module>
from rest_framework.schemas import DefaultSchema
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework/schemas/__init__.py", line 32, in <module>
authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES,
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework/settings.py", line 227, in __getattr__
val = perform_import(val, attr)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework/settings.py", line 172, in perform_import
return [import_from_string(item, setting_name) for item in val]
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework/settings.py", line 172, in <listcomp>
return [import_from_string(item, setting_name) for item in val]
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework/settings.py", line 183, in import_from_string
module = import_module(module_path)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework_simplejwt/authentication.py", line 5, in <module>
from .models import TokenUser
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/rest_framework_simplejwt/models.py", line 1, in <module>
from django.contrib.auth import models as auth_models
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/contrib/auth/models.py", line 2, in <module>
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/contrib/auth/base_user.py", line 47, in <module>
class AbstractBaseUser(models.Model):
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/db/models/base.py", line 87, in __new__
app_config = apps.get_containing_app_config(module)
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/apps/registry.py", line 249, in get_containing_app_config
self.check_apps_ready()
File "/home/a-shestakov/Projects/test_project/venv/lib/python3.6/site-packages/django/apps/registry.py", line 132, in check_apps_ready
raise AppRegistryNotReady("Apps aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
Temporary solution for me is to exclude rest_registration
from INSTALLED_APPS
, include views to urlconf and provide required email templates manually. But it would be nice if this was fixed.
Thanks,
Anton
Class-based views greatly simplify adding extra logic into views. I am therefore proposing to refactor the views into class-based views.
Here are some examples why this is useful, at least as far as the user registration view is concerned:
1.)
For user registration, I would like to record the client IP address. This is best done in the register
view, by calling serializer_class()
with an extra context
argument, like serializer_class(data=request.data, context={'request': request})
. This way, the serializer can access self.context['request'].META.get('REMOTE_ADDR')
and populate a field on the model instance it creates.
As far as I can see, this is currently not possible, except by replacing the view by a copy of it and modifying it. This violates the DRY principle and is also detached from future upstream changes in this view.
If the registration view was a class-based view inheriting from DRF's GenericAPIView
(e.g. a CreateAPIView
), the following few lines of code would be sufficient to solve this (assuming the upstream registration view is called DefaultRegisterUserView
):
class RegisterUserView(DefaultRegisterUserView):
def get_serializer_context(self):
return {'request': self.request}
class RegisterUserSerializer(DefaultRegisterUserSerializer):
def create(self, validated_data):
data = validated_data.copy()
data['registration_remote_ip'] = self.context['request'].META.get('REMOTE_ADDR')
return super().create(data)
... plus the two lines in urls.py
and settings.py
where the view and the serializer are configured.
2.)
Also, one may consider changing the response of the registration view such that information like "user with this email exists" is not leaked. Instead, 202 Accepted
could be an appropriate status code in all cases, with the view redirecting to the password reset view if the email address is already taken, and otherwise sending the regular registration verification email.
(It would be the frontend's responsibility to react with a proper message, like "We have sent you an email with further instructions", which would be true in all cases.)
This way, one could expose a unified register+reset endpoint which acts as a general "give me access to an account with this email address" endpoint. The frontend could of course present it in different variations for registration and for password reset purposes.
I am not proposing that these endpoints be merged (that would be another interesting issue); I am rather making the point that if the registration endpoint was a class-based view, one could easily achieve the above with something like (rough sketch):
class RegisterUserView(DefaultRegisterUserView):
def post(request, *args, **kwargs):
try:
# Try to register user as usual
return super().post(request, *args, *kwargs)
except ValidationError as e:
# Perform password reset if account exists
email_error = e.message_dict.get('email')
if email_error and email_error.endswith(' already exists.'):
return PasswortResetView.as_view()(request)
# For other errors, continue as usual
raise e
Currently only change password view don't have the option to disable password_confirm field.
The schema of the view cannot be seen on the swagger page. This problem was mentioned in issue #7 .
With my custom login view I get the user data after successful login via the login endpoint. Unfortunately with django-rest-registration all I get is:
{ "detail": "Login successful" }
Which means that I have to make another request to the profile endpoint to completely login a user in my frontend. Is this how it should be done? It seems unnecessary to do 2 request if it could be done in one. Curious to hear how others handle this.
hi all,
I want after user click to reset password link in email and set new password then the reset password link will be expired. How can i achieve it?
Thanks.
I followed the basic steps and things were fine until I tried to connect my backend API with ReactJs and this error showed off saying that "Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers"
App.js
axios({method:'post',url: 'http://127.0.0.1:8000/a/login/',headers: { 'Access-Control-Allow-Origin': '*', }}) .then(res => { const user = res.data; this.setState({ user }); console.log(res.data) })
settings.py
CORS_ORIGIN_ALLOW_ALL = True
But, when I change the URL into a different url of mine things works perfect with GET
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.