-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b4d77fe
Showing
30 changed files
with
994 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: Code Lint | ||
|
||
on: | ||
push: | ||
paths: | ||
- "**.py" | ||
|
||
jobs: | ||
lint: | ||
name: Python Lint | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: "3.10" | ||
- name: Run flake8 | ||
uses: julianwachholz/flake8-action@v2 | ||
with: | ||
checkName: "Python Lint" | ||
plugins: flake8-black | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.py[co] | ||
*.sqlite3 | ||
poetry.lock | ||
/dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright (c) 2021 Julian Wachholz | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
# django-guest-user | ||
|
||
A Django app that allows visitors to interact with your site as a guest user | ||
without requiring registration. | ||
|
||
Largely inspired by [django-lazysignup](https://github.com/danfairs/django-lazysignup) and rewritten for Django 3 and Python 3.6 and up. | ||
|
||
## Installation | ||
|
||
Install the package with your favorite package manager from PyPI: | ||
|
||
``` | ||
pip install django-guest-user | ||
``` | ||
|
||
Add the app to your `INSTALLED_APPS` and `AUTHENTICATION_BACKENDS`: | ||
|
||
```python | ||
INSTALLED_APPS = [ | ||
... | ||
"django_guest_user", | ||
] | ||
|
||
AUTHENTICATION_BACKENDS = [ | ||
"django.contrib.auth.backends.ModelBackend", | ||
"guest_user.backends.GuestBackend", | ||
] | ||
``` | ||
|
||
Add the patterns to your URL config: | ||
|
||
```python | ||
urlpatterns = [ | ||
... | ||
path("convert/", include("guest_user.urls")), | ||
] | ||
``` | ||
|
||
Don't forget to run migrations: | ||
|
||
``` | ||
python manage.py migrate | ||
``` | ||
|
||
## How to use | ||
|
||
Guest users are not created for every unauthenticated request. | ||
Instead, use the `@allow_guest_user` decorator on a view to enable | ||
that view to be accessed by a temporary guest user. | ||
|
||
```python | ||
from guest_user.decorators import allow_guest_user | ||
|
||
@allow_guest_user | ||
def my_view(request): | ||
# Will always be either a registered a guest user. | ||
username = request.user.username | ||
return HttpResponse(f"Hello, {username}!") | ||
``` | ||
|
||
## API | ||
|
||
### `@guest_user.decorators.allow_guest_user` | ||
|
||
View decorator that will create a temporary guest user in the event | ||
that the decorated view is accessed by an unauthenticated visitor. | ||
|
||
Takes no arguments. | ||
|
||
### `@guest_user.decorators.guest_user_required(redirect_field_name="next", login_url=None)` | ||
|
||
View decorator that redirects to a given URL if the accessing user is | ||
anonymous or already authenticated. | ||
|
||
Arguments: | ||
|
||
- `redirect_field_name`: URL query parameter to use to link back in the case of a redirect to the login url. Defaults to `django.contrib.auth.REDIRECT_FIELD_NAME` ("next"). | ||
- `login_url`: URL to redirect to if the user is not authenticated. Defaults to the `LOGIN_URL` setting. | ||
|
||
### `@guest_user.decorators.regular_user_required(redirect_field_name="next", login_url=None)` | ||
|
||
Decorator that will not allow guest users to access the view. | ||
Will redirect to the conversion page to allow a guest user to fully register. | ||
|
||
Arguments: | ||
|
||
- `redirect_field_name`: URL query parameter to use to link back in the case of a redirect to the login url. Defaults to `django.contrib.auth.REDIRECT_FIELD_NAME` ("next"). | ||
- `login_url`: URL to redirect to if the user is a guest. Defaults to `"guest_user_convert"`. | ||
|
||
### `guest_user.functions.get_guest_model()` | ||
|
||
The guest user model is swappable. This function will return the currently configured model class. | ||
|
||
### `guest_user.functions.is_guest_user(user)` | ||
|
||
Check wether the given user instance is a temporary guest. | ||
|
||
### `guest_user.signals.converted` | ||
|
||
Signal that is dispatched when a guest user is converted to a regular user. | ||
|
||
### Template tag `is_guest_user` | ||
|
||
A filter to use in templates to check if the user object is a guest. | ||
|
||
``` | ||
{% load guest_user %} | ||
{% if user|is_guest_user %} | ||
Hello guest. | ||
{% endif %} | ||
``` | ||
|
||
## Settings | ||
|
||
Various settings are provided to allow customization of the guest user behavior. | ||
|
||
### `GUEST_USER_ENABLED` | ||
|
||
`bool`. If `False`, the `@allow_guest_user` decorator will not create guest users. | ||
Defaults to `True`. | ||
|
||
### `GUEST_USER_MODEL` | ||
|
||
`str`. The swappable model identifier to use as the guest model. | ||
Defaults to `"guest_user.Guest"`. | ||
|
||
### `GUEST_USER_NAME_GENERATOR` | ||
|
||
`str`. Import path to a function that will generate a username for a guest user. | ||
Defaults to `"guest_user.functions.generate_uuid_username"`. | ||
|
||
Included with the package are two alternatives: | ||
|
||
`"guest_user.functions.generate_numbered_username"`: Will create a random four digit | ||
number prefixed by `GUEST_USER_NAME_PREFIX`. | ||
|
||
`"guest_user.functions.generate_friendly_username"`: Creates a friendly and easy to remember username by combining an adjective, noun and number. Requires `random_username` to be installed. | ||
|
||
### `GUEST_USER_NAME_PREFIX` | ||
|
||
`str`. A prefix to use with the `generate_numbered_username` generator. | ||
Defaults to `"Guest"`. | ||
|
||
### `GUEST_USER_CONVERT_FORM` | ||
|
||
`str`. Import path for the guest conversion form. | ||
Must implement `get_credentials` to be passed to Django's `authenticate` function. | ||
Defaults to `"guest_user.forms.UserCreationForm"`. | ||
|
||
### `GUEST_USER_CONVERT_PREFILL_USERNAME` | ||
|
||
`bool`. Set the generated username as initial value on the conversion form. | ||
Defaults to `False`. | ||
|
||
### `GUEST_USER_CONVERT_URL` | ||
|
||
`str`. URL name for the convert view. | ||
Defaults to `"guest_user_convert"`. | ||
|
||
### `GUEST_USER_CONVERT_REDIRECT_URL` | ||
|
||
`str`. URL name to redirect to after conversion, unless a redirect parameter was provided. | ||
Defaults to `"guest_user_convert_success"`. | ||
|
||
### `GUEST_USER_BLOCKED_USER_AGENTS` | ||
|
||
`list[str]`. Web crawlers and other user agents to block from becoming guest users. | ||
The list will be combined into a regular expression. | ||
Default includes a number of well known bots and spiders. | ||
|
||
## Status | ||
|
||
This project is currently untested. But thanks to [previous work](https://github.com/danfairs/django-lazysignup) it is largely functional. | ||
|
||
I decided to rewrite the project since the original project hasn't seen any | ||
larger updates for a few years now and the code base was written a long time ago. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from django.contrib import admin | ||
|
||
from .models import Guest | ||
|
||
|
||
@admin.register(Guest) | ||
class GuestAdmin(admin.ModelAdmin): | ||
list_display = ["user", "created_at"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class GuestUserConfig(AppConfig): | ||
name = "guest_user" | ||
verbose_name = "Guest User" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from django.contrib.auth import get_user_model | ||
from django.contrib.auth.backends import ModelBackend | ||
|
||
|
||
class GuestBackend(ModelBackend): | ||
def authenticate(self, request, username=None, password=None, **kwargs): | ||
"""Authenticate with username only.""" | ||
UserModel = get_user_model() | ||
|
||
try: | ||
return UserModel.objects.get(**{UserModel.USERNAME_FIELD: username}) | ||
except UserModel.DoesNotExist: | ||
return None | ||
|
||
def get_user(self, user_id): | ||
UserModel = get_user_model() | ||
try: | ||
user = UserModel._default_manager.get(pk=user_id) | ||
# user.backend = "guest_user.backends.GuestBackend" | ||
except UserModel.DoesNotExist: | ||
return None | ||
return user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
from functools import wraps | ||
|
||
from django.contrib.auth import REDIRECT_FIELD_NAME, authenticate, login | ||
from django.contrib.auth.decorators import user_passes_test | ||
|
||
from . import settings | ||
from .functions import get_guest_model, is_guest_user | ||
|
||
|
||
def allow_guest_user(function=None): | ||
""" | ||
Allow anonymous users to access the view by creating a guest user. | ||
""" | ||
|
||
def wrapped(request, *args, **kwargs): | ||
assert hasattr( | ||
request, "session" | ||
), "Please add 'django.contrib.sessions' to INSTALLED_APPS." | ||
|
||
if settings.ENABLED and request.user.is_anonymous: | ||
user_agent = request.META.get("HTTP_USER_AGENT", "") | ||
|
||
if not settings.BLOCKED_USER_AGENTS.match(user_agent): | ||
Guest = get_guest_model() | ||
user = Guest.objects.create_guest_user() | ||
# request.user = None | ||
user = authenticate(username=user.username) | ||
assert user, ( | ||
"Guest authentication failed. Do you have " | ||
"'guest_user.backends.GuestBackend' in AUTHENTICATION_BACKENDS?" | ||
) | ||
login(request, user) | ||
|
||
return function(request, *args, **kwargs) | ||
|
||
return wraps(function)(wrapped) | ||
|
||
|
||
def guest_user_required( | ||
function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None | ||
): | ||
""" | ||
Current user must be a temporary guest. | ||
Other visitors will be redirected to `login_url` or a redirect parameter given in the URL. | ||
""" | ||
actual_decorator = user_passes_test( | ||
is_guest_user, | ||
login_url=login_url, | ||
redirect_field_name=redirect_field_name, | ||
) | ||
|
||
if function: | ||
return actual_decorator(function) | ||
return actual_decorator | ||
|
||
|
||
def regular_user_required( | ||
function=None, redirect_field_name=REDIRECT_FIELD_NAME, convert_url=None | ||
): | ||
""" | ||
Current user must not be a temporary guest. | ||
Guest users will be redirected to the convert page. | ||
""" | ||
if convert_url is None: | ||
convert_url = settings.CONVERT_URL | ||
|
||
actual_decorator = user_passes_test( | ||
lambda u: u.is_authenticated and not is_guest_user(u), | ||
login_url=convert_url, | ||
redirect_field_name=redirect_field_name, | ||
) | ||
if function: | ||
return actual_decorator(function) | ||
return actual_decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
class NotGuestError(TypeError): | ||
"""Raised when an operation is attempted on a non-lazy user""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm | ||
|
||
|
||
class UserCreationForm(BaseUserCreationForm): | ||
def get_credentials(self): | ||
return { | ||
"username": self.cleaned_data["username"], | ||
"password": self.cleaned_data["password1"], | ||
} |
Oops, something went wrong.