././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1603274433.3979297
djoser-2.1.0/LICENSE 0000644 0000000 0000000 00000002073 00000000000 010676 0 ustar 00 The MIT License (MIT)
Copyright (c) 2013-2019 SUNSCRAPERS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1603449460.355233
djoser-2.1.0/README.rst 0000644 0000000 0000000 00000011171 00000000000 011357 0 ustar 00 ======
djoser
======
.. image:: https://img.shields.io/pypi/v/djoser.svg
:target: https://pypi.org/project/djoser
.. image:: https://img.shields.io/travis/sunscrapers/djoser/master.svg
:target: https://travis-ci.org/sunscrapers/djoser
.. image:: https://img.shields.io/codecov/c/github/sunscrapers/djoser.svg
:target: https://codecov.io/gh/sunscrapers/djoser
.. image:: https://api.codacy.com/project/badge/Grade/c9bf80318d2741e5bb63912a5e0b32dc
:alt: Codacy Badge
:target: https://app.codacy.com/app/dekoza/djoser?utm_source=github.com&utm_medium=referral&utm_content=sunscrapers/djoser&utm_campaign=Badge_Grade_Dashboard
.. image:: https://img.shields.io/pypi/dm/djoser
:target: https://img.shields.io/pypi/dm/djoser
REST implementation of `Django `_ authentication
system. **djoser** library provides a set of `Django Rest Framework `_
views to handle basic actions such as registration, login, logout, password
reset and account activation. It works with
`custom user model `_.
Instead of reusing Django code (e.g. ``PasswordResetForm``), we reimplemented
few things to fit better into `Single Page App `_
architecture.
Developed by `SUNSCRAPERS `_ with passion & patience.
.. image:: https://asciinema.org/a/94J4eG2tSBD2iEfF30a6vGtXw.png
:target: https://asciinema.org/a/94J4eG2tSBD2iEfF30a6vGtXw
Requirements
============
To be able to run **djoser** you have to meet following requirements:
- Python (3.6, 3.7, 3.8, 3.9)
- Django (2.2, 3.1)
- Django REST Framework 3.11.1
If you need to support other versions, please use djoser<2.
Installation
============
Simply install using ``pip``:
.. code-block:: bash
$ pip install djoser
And continue with the steps described at
`configuration `_
guide.
Documentation
=============
Documentation is available to study at
`https://djoser.readthedocs.io `_
and in ``docs`` directory.
Contributing and development
============================
To start developing on **djoser**, clone the repository:
.. code-block:: bash
$ git clone git@github.com:sunscrapers/djoser.git
We use `poetry `_ as dependency management and packaging tool.
.. code-block:: bash
$ cd djoser
$ poetry install -E test
This will create a virtualenv with all development dependencies.
To run the test just type:
.. code-block:: bash
$ poetry run py.test testproject
We also preapred a convenient ``Makefile`` to automate commands above:
.. code-block:: bash
$ make init
$ make test
To activate the virtual environment run
.. code-block:: bash
$ poetry shell
Without poetry
--------------
New versions of ``pip`` can use ``pyproject.toml`` to build the package and install its dependencies.
.. code-block:: bash
$ pip install .[test]
.. code-block:: bash
$ cd testproject
$ ./manage.py test
Tox
---
If you need to run tests against all supported Python and Django versions then invoke:
.. code-block:: bash
$ poetry run tox -p all
Example project
---------------
You can also play with test project by running following commands:
.. code-block:: bash
$ make migrate
$ make runserver
Commiting your code
-------------------
Before sending patches please make sure you have `pre-commit `_ activated in your local git repository:
.. code-block:: bash
$ pre-commit install
This will ensure that your code is cleaned before you commit it.
Some steps (like black) automatically fix issues but the show their status as FAILED.
Just inspect if eveything is OK, git-add the files and retry the commit.
Other tools (like flake8) require you to manually fix the issues.
Similar projects
================
List of projects related to Django, REST and authentication:
- `django-rest-framework-simplejwt `_
- `django-oauth-toolkit `_
- `django-rest-auth `_ (not maintained)
- `django-rest-framework-digestauth `_ (not maintained)
Please, keep in mind that while using custom authentication and TokenCreateSerializer
validation, there is a path that **ignores intentional return of None** from authenticate()
and try to find User using parameters. Probably, that will be changed in the future.
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1603274433.3993125
djoser-2.1.0/djoser/.codacy.yml 0000644 0000000 0000000 00000000062 00000000000 013216 0 ustar 00 exclude_paths:
- "docs/**"
- "testproject/**"
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1603274433.3994265
djoser-2.1.0/djoser/__init__.py 0000644 0000000 0000000 00000000247 00000000000 013271 0 ustar 00 try:
import importlib.metadata as importlib_metadata
except ModuleNotFoundError:
import importlib_metadata
__version__ = importlib_metadata.version(__name__)
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1603274433.399533
djoser-2.1.0/djoser/compat.py 0000644 0000000 0000000 00000000413 00000000000 013010 0 ustar 00 from djoser.conf import settings
__all__ = ["settings"]
def get_user_email(user):
email_field_name = get_user_email_field_name(user)
return getattr(user, email_field_name, None)
def get_user_email_field_name(user):
return user.get_email_field_name()
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1603274433.3996687
djoser-2.1.0/djoser/conf.py 0000644 0000000 0000000 00000014625 00000000000 012464 0 ustar 00 from django.apps import apps
from django.conf import settings as django_settings
from django.test.signals import setting_changed
from django.utils.functional import LazyObject
from django.utils.module_loading import import_string
DJOSER_SETTINGS_NAMESPACE = "DJOSER"
auth_module, user_model = django_settings.AUTH_USER_MODEL.rsplit(".", 1)
User = apps.get_model(auth_module, user_model)
class ObjDict(dict):
def __getattribute__(self, item):
try:
val = self[item]
if isinstance(val, str):
val = import_string(val)
elif isinstance(val, (list, tuple)):
val = [import_string(v) if isinstance(v, str) else v for v in val]
self[item] = val
except KeyError:
val = super(ObjDict, self).__getattribute__(item)
return val
default_settings = {
"USER_ID_FIELD": User._meta.pk.name,
"LOGIN_FIELD": User.USERNAME_FIELD,
"SEND_ACTIVATION_EMAIL": False,
"SEND_CONFIRMATION_EMAIL": False,
"USER_CREATE_PASSWORD_RETYPE": False,
"SET_PASSWORD_RETYPE": False,
"PASSWORD_RESET_CONFIRM_RETYPE": False,
"SET_USERNAME_RETYPE": False,
"USERNAME_RESET_CONFIRM_RETYPE": False,
"PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND": False,
"USERNAME_RESET_SHOW_EMAIL_NOT_FOUND": False,
"PASSWORD_CHANGED_EMAIL_CONFIRMATION": False,
"USERNAME_CHANGED_EMAIL_CONFIRMATION": False,
"TOKEN_MODEL": "rest_framework.authtoken.models.Token",
"SERIALIZERS": ObjDict(
{
"activation": "djoser.serializers.ActivationSerializer",
"password_reset": "djoser.serializers.SendEmailResetSerializer",
"password_reset_confirm": "djoser.serializers.PasswordResetConfirmSerializer",
"password_reset_confirm_retype": "djoser.serializers.PasswordResetConfirmRetypeSerializer",
"set_password": "djoser.serializers.SetPasswordSerializer",
"set_password_retype": "djoser.serializers.SetPasswordRetypeSerializer",
"set_username": "djoser.serializers.SetUsernameSerializer",
"set_username_retype": "djoser.serializers.SetUsernameRetypeSerializer",
"username_reset": "djoser.serializers.SendEmailResetSerializer",
"username_reset_confirm": "djoser.serializers.UsernameResetConfirmSerializer",
"username_reset_confirm_retype": "djoser.serializers.UsernameResetConfirmRetypeSerializer",
"user_create": "djoser.serializers.UserCreateSerializer",
"user_create_password_retype": "djoser.serializers.UserCreatePasswordRetypeSerializer",
"user_delete": "djoser.serializers.UserDeleteSerializer",
"user": "djoser.serializers.UserSerializer",
"current_user": "djoser.serializers.UserSerializer",
"token": "djoser.serializers.TokenSerializer",
"token_create": "djoser.serializers.TokenCreateSerializer",
}
),
"EMAIL": ObjDict(
{
"activation": "djoser.email.ActivationEmail",
"confirmation": "djoser.email.ConfirmationEmail",
"password_reset": "djoser.email.PasswordResetEmail",
"password_changed_confirmation": "djoser.email.PasswordChangedConfirmationEmail",
"username_changed_confirmation": "djoser.email.UsernameChangedConfirmationEmail",
"username_reset": "djoser.email.UsernameResetEmail",
}
),
"CONSTANTS": ObjDict({"messages": "djoser.constants.Messages"}),
"LOGOUT_ON_PASSWORD_CHANGE": False,
"CREATE_SESSION_ON_LOGIN": False,
"SOCIAL_AUTH_TOKEN_STRATEGY": "djoser.social.token.jwt.TokenStrategy",
"SOCIAL_AUTH_ALLOWED_REDIRECT_URIS": [],
"HIDE_USERS": True,
"PERMISSIONS": ObjDict(
{
"activation": ["rest_framework.permissions.AllowAny"],
"password_reset": ["rest_framework.permissions.AllowAny"],
"password_reset_confirm": ["rest_framework.permissions.AllowAny"],
"set_password": ["djoser.permissions.CurrentUserOrAdmin"],
"username_reset": ["rest_framework.permissions.AllowAny"],
"username_reset_confirm": ["rest_framework.permissions.AllowAny"],
"set_username": ["djoser.permissions.CurrentUserOrAdmin"],
"user_create": ["rest_framework.permissions.AllowAny"],
"user_delete": ["djoser.permissions.CurrentUserOrAdmin"],
"user": ["djoser.permissions.CurrentUserOrAdmin"],
"user_list": ["djoser.permissions.CurrentUserOrAdmin"],
"token_create": ["rest_framework.permissions.AllowAny"],
"token_destroy": ["rest_framework.permissions.IsAuthenticated"],
}
),
}
SETTINGS_TO_IMPORT = ["TOKEN_MODEL", "SOCIAL_AUTH_TOKEN_STRATEGY"]
class Settings:
def __init__(self, default_settings, explicit_overriden_settings: dict = None):
if explicit_overriden_settings is None:
explicit_overriden_settings = {}
overriden_settings = (
getattr(django_settings, DJOSER_SETTINGS_NAMESPACE, {})
or explicit_overriden_settings
)
self._load_default_settings()
self._override_settings(overriden_settings)
self._init_settings_to_import()
def _load_default_settings(self):
for setting_name, setting_value in default_settings.items():
if setting_name.isupper():
setattr(self, setting_name, setting_value)
def _override_settings(self, overriden_settings: dict):
for setting_name, setting_value in overriden_settings.items():
value = setting_value
if isinstance(setting_value, dict):
value = getattr(self, setting_name, {})
value.update(ObjDict(setting_value))
setattr(self, setting_name, value)
def _init_settings_to_import(self):
for setting_name in SETTINGS_TO_IMPORT:
value = getattr(self, setting_name)
if isinstance(value, str):
setattr(self, setting_name, import_string(value))
class LazySettings(LazyObject):
def _setup(self, explicit_overriden_settings=None):
self._wrapped = Settings(default_settings, explicit_overriden_settings)
settings = LazySettings()
def reload_djoser_settings(*args, **kwargs):
global settings
setting, value = kwargs["setting"], kwargs["value"]
if setting == DJOSER_SETTINGS_NAMESPACE:
settings._setup(explicit_overriden_settings=value)
setting_changed.connect(reload_djoser_settings)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1603274433.3998125
djoser-2.1.0/djoser/constants.py 0000644 0000000 0000000 00000001330 00000000000 013540 0 ustar 00 from django.utils.translation import gettext_lazy as _
class Messages(object):
INVALID_CREDENTIALS_ERROR = _("Unable to log in with provided credentials.")
INACTIVE_ACCOUNT_ERROR = _("User account is disabled.")
INVALID_TOKEN_ERROR = _("Invalid token for given user.")
INVALID_UID_ERROR = _("Invalid user id or user doesn't exist.")
STALE_TOKEN_ERROR = _("Stale token for given user.")
PASSWORD_MISMATCH_ERROR = _("The two password fields didn't match.")
USERNAME_MISMATCH_ERROR = _("The two {0} fields didn't match.")
INVALID_PASSWORD_ERROR = _("Invalid password.")
EMAIL_NOT_FOUND = _("User with given email does not exist.")
CANNOT_CREATE_USER_ERROR = _("Unable to create account.")
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1603274433.3999977
djoser-2.1.0/djoser/email.py 0000644 0000000 0000000 00000003505 00000000000 012621 0 ustar 00 from django.contrib.auth.tokens import default_token_generator
from templated_mail.mail import BaseEmailMessage
from djoser import utils
from djoser.conf import settings
class ActivationEmail(BaseEmailMessage):
template_name = "email/activation.html"
def get_context_data(self):
# ActivationEmail can be deleted
context = super().get_context_data()
user = context.get("user")
context["uid"] = utils.encode_uid(user.pk)
context["token"] = default_token_generator.make_token(user)
context["url"] = settings.ACTIVATION_URL.format(**context)
return context
class ConfirmationEmail(BaseEmailMessage):
template_name = "email/confirmation.html"
class PasswordResetEmail(BaseEmailMessage):
template_name = "email/password_reset.html"
def get_context_data(self):
# PasswordResetEmail can be deleted
context = super().get_context_data()
user = context.get("user")
context["uid"] = utils.encode_uid(user.pk)
context["token"] = default_token_generator.make_token(user)
context["url"] = settings.PASSWORD_RESET_CONFIRM_URL.format(**context)
return context
class PasswordChangedConfirmationEmail(BaseEmailMessage):
template_name = "email/password_changed_confirmation.html"
class UsernameChangedConfirmationEmail(BaseEmailMessage):
template_name = "email/username_changed_confirmation.html"
class UsernameResetEmail(BaseEmailMessage):
template_name = "email/username_reset.html"
def get_context_data(self):
context = super().get_context_data()
user = context.get("user")
context["uid"] = utils.encode_uid(user.pk)
context["token"] = default_token_generator.make_token(user)
context["url"] = settings.USERNAME_RESET_CONFIRM_URL.format(**context)
return context
././@PaxHeader 0000000 0000000 0000000 00000000031 00000000000 010207 x ustar 00 25 mtime=1603443965.2944
djoser-2.1.0/djoser/locale/de/LC_MESSAGES/django.mo 0000644 0000000 0000000 00000006142 00000000000 016373 0 ustar 00 I #
&