mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-30 13:31:08 +00:00
Merge branch 'main' into pylint-update
This commit is contained in:
commit
be86fbcf4d
21 changed files with 268 additions and 24 deletions
2
.github/workflows/pylint.yml
vendored
2
.github/workflows/pylint.yml
vendored
|
@ -24,5 +24,5 @@ jobs:
|
||||||
pip install pylint
|
pip install pylint
|
||||||
- name: Analysing the code with pylint
|
- name: Analysing the code with pylint
|
||||||
run: |
|
run: |
|
||||||
pylint bookwyrm/ --ignore=migrations,tests --disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801
|
pylint bookwyrm/ --ignore=migrations,tests --disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801,C0209
|
||||||
|
|
||||||
|
|
|
@ -311,6 +311,12 @@ class EmailBlocklistForm(CustomForm):
|
||||||
fields = ["domain"]
|
fields = ["domain"]
|
||||||
|
|
||||||
|
|
||||||
|
class IPBlocklistForm(CustomForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.IPBlocklist
|
||||||
|
fields = ["address"]
|
||||||
|
|
||||||
|
|
||||||
class ServerForm(CustomForm):
|
class ServerForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.FederatedServer
|
model = models.FederatedServer
|
||||||
|
|
3
bookwyrm/middleware/__init__.py
Normal file
3
bookwyrm/middleware/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
""" look at all this nice middleware! """
|
||||||
|
from .timezone_middleware import TimezoneMiddleware
|
||||||
|
from .ip_middleware import IPBlocklistMiddleware
|
16
bookwyrm/middleware/ip_middleware.py
Normal file
16
bookwyrm/middleware/ip_middleware.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
""" Block IP addresses """
|
||||||
|
from django.http import Http404
|
||||||
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
|
class IPBlocklistMiddleware:
|
||||||
|
"""check incoming traffic against an IP block-list"""
|
||||||
|
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
address = request.META.get("REMOTE_ADDR")
|
||||||
|
if models.IPBlocklist.objects.filter(address=address).exists():
|
||||||
|
raise Http404()
|
||||||
|
return self.get_response(request)
|
38
bookwyrm/migrations/0097_auto_20210917_1858.py
Normal file
38
bookwyrm/migrations/0097_auto_20210917_1858.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-09-17 18:58
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0096_merge_20210912_0044"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="IPBlocklist",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created_date", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("address", models.CharField(max_length=255, unique=True)),
|
||||||
|
("is_active", models.BooleanField(default=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"ordering": ("-created_date",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="emailblocklist",
|
||||||
|
name="is_active",
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -26,8 +26,8 @@ from .import_job import ImportJob, ImportItem
|
||||||
|
|
||||||
from .site import SiteSettings, SiteInvite
|
from .site import SiteSettings, SiteInvite
|
||||||
from .site import PasswordReset, InviteRequest
|
from .site import PasswordReset, InviteRequest
|
||||||
from .site import EmailBlocklist
|
|
||||||
from .announcement import Announcement
|
from .announcement import Announcement
|
||||||
|
from .antispam import EmailBlocklist, IPBlocklist
|
||||||
|
|
||||||
cls_members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
|
cls_members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
|
||||||
activity_models = {
|
activity_models = {
|
||||||
|
|
35
bookwyrm/models/antispam.py
Normal file
35
bookwyrm/models/antispam.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
""" Lets try NOT to sell viagra """
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from .user import User
|
||||||
|
|
||||||
|
|
||||||
|
class EmailBlocklist(models.Model):
|
||||||
|
"""blocked email addresses"""
|
||||||
|
|
||||||
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
domain = models.CharField(max_length=255, unique=True)
|
||||||
|
is_active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""default sorting"""
|
||||||
|
|
||||||
|
ordering = ("-created_date",)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def users(self):
|
||||||
|
"""find the users associated with this address"""
|
||||||
|
return User.objects.filter(email__endswith=f"@{self.domain}")
|
||||||
|
|
||||||
|
|
||||||
|
class IPBlocklist(models.Model):
|
||||||
|
"""blocked ip addresses"""
|
||||||
|
|
||||||
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
address = models.CharField(max_length=255, unique=True)
|
||||||
|
is_active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""default sorting"""
|
||||||
|
|
||||||
|
ordering = ("-created_date",)
|
|
@ -124,23 +124,6 @@ class PasswordReset(models.Model):
|
||||||
return "https://{}/password-reset/{}".format(DOMAIN, self.code)
|
return "https://{}/password-reset/{}".format(DOMAIN, self.code)
|
||||||
|
|
||||||
|
|
||||||
class EmailBlocklist(models.Model):
|
|
||||||
"""blocked email addresses"""
|
|
||||||
|
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
|
||||||
domain = models.CharField(max_length=255, unique=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""default sorting"""
|
|
||||||
|
|
||||||
ordering = ("-created_date",)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def users(self):
|
|
||||||
"""find the users associated with this address"""
|
|
||||||
return User.objects.filter(email__endswith=f"@{self.domain}")
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
@receiver(models.signals.post_save, sender=SiteSettings)
|
@receiver(models.signals.post_save, sender=SiteSettings)
|
||||||
def preview_image(instance, *args, **kwargs):
|
def preview_image(instance, *args, **kwargs):
|
||||||
|
|
|
@ -77,7 +77,8 @@ MIDDLEWARE = [
|
||||||
"django.middleware.common.CommonMiddleware",
|
"django.middleware.common.CommonMiddleware",
|
||||||
"django.middleware.csrf.CsrfViewMiddleware",
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"bookwyrm.timezone_middleware.TimezoneMiddleware",
|
"bookwyrm.middleware.TimezoneMiddleware",
|
||||||
|
"bookwyrm.middleware.IPBlocklistMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
|
|
|
@ -378,6 +378,13 @@ input[type=file]::file-selector-button:hover {
|
||||||
right: 1em;
|
right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Tooltips
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/** States
|
/** States
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% trans "Help" as button_text %}
|
{% trans "Help" as button_text %}
|
||||||
{% include 'snippets/toggle/open_button.html' with text=button_text class="ml-3 is-rounded is-small is-white p-0 pb-1" icon="question-circle is-size-6" controls_text=controls_text controls_uid=controls_uid %}
|
{% include 'snippets/toggle/open_button.html' with text=button_text class="ml-3 is-rounded is-small is-white p-0 pb-1" icon="question-circle is-size-6" controls_text=controls_text controls_uid=controls_uid %}
|
||||||
|
|
||||||
<aside class="notification is-hidden transition-y is-pulled-left mb-2" id="{{ controls_text }}{% if controls_uid %}-{{ controls_uid }}{% endif %}">
|
<aside class="tooltip notification is-hidden transition-y is-pulled-left mb-2" id="{{ controls_text }}{% if controls_uid %}-{{ controls_uid }}{% endif %}">
|
||||||
{% trans "Close" as button_text %}
|
{% trans "Close" as button_text %}
|
||||||
{% include 'snippets/toggle/close_button.html' with label=button_text class="delete" nonbutton=True controls_text=controls_text controls_uid=controls_uid %}
|
{% include 'snippets/toggle/close_button.html' with label=button_text class="delete" nonbutton=True controls_text=controls_text controls_uid=controls_uid %}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{% block form %}
|
{% block form %}
|
||||||
<form name="add-domain" method="post" action="{% url 'settings-email-blocks' %}">
|
<form name="add-domain" method="post" action="{% url 'settings-email-blocks' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<label class="label" for="id_event_date">{% trans "Domain:" %}</label>
|
<label class="label" for="id_domain">{% trans "Domain:" %}</label>
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="button is-disabled">@</div>
|
<div class="button is-disabled">@</div>
|
||||||
|
|
36
bookwyrm/templates/settings/ip_address_form.html
Normal file
36
bookwyrm/templates/settings/ip_address_form.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends 'components/inline_form.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
{% trans "Add IP address" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
<form name="add-address" method="post" action="{% url 'settings-ip-blocks' %}">
|
||||||
|
<div class="block">
|
||||||
|
{% trans "Use IP address blocks with caution, and consider using blocks only temporarily, as IP addresses are often shared or change hands. If you block your own IP, you will not be able to access this page." %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_address">
|
||||||
|
{% trans "IP Address:" %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<input type="text" name="address" maxlength="255" class="input" required="" id="id_address" placeholder="190.0.2.0/24">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% for error in form.address.errors %}
|
||||||
|
<p class="help is-danger">{{ error | escape }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<button type="submit" class="button is-primary">{% trans "Add" %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
47
bookwyrm/templates/settings/ip_blocklist.html
Normal file
47
bookwyrm/templates/settings/ip_blocklist.html
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{% extends 'settings/layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "IP Address Blocklist" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}{% trans "IP Address Blocklist" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block edit-button %}
|
||||||
|
{% trans "Add IP address" as button_text %}
|
||||||
|
{% include 'snippets/toggle/open_button.html' with controls_text="add_address" icon_with_text="plus" text=button_text focus="add_address_header" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block panel %}
|
||||||
|
{% include 'settings/ip_address_form.html' with controls_text="add_address" class="block" %}
|
||||||
|
|
||||||
|
<p class="notification block">
|
||||||
|
{% trans "Any traffic from this IP address will get a 404 response when trying to access any part of the application." %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table class="table is-striped is-fullwidth">
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
{% trans "Address" %}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{% trans "Options" %}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
{% for address in addresses %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ address.address }}</td>
|
||||||
|
<td>
|
||||||
|
<form name="remove-{{ address.id }}" action="{% url 'settings-ip-blocks-delete' address.id %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% trans "Delete" as button_text %}
|
||||||
|
<button class="button" type="submit">
|
||||||
|
<span class="icon icon-x" title="{{ button_text }}" aria-hidden="true"></span>
|
||||||
|
<span class="is-hidden-mobile">{{ button_text }}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
8
bookwyrm/templates/settings/ip_tooltip.html
Normal file
8
bookwyrm/templates/settings/ip_tooltip.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends 'components/tooltip.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block tooltip_content %}
|
||||||
|
|
||||||
|
{% trans "You can block IP ranges using CIDR syntax." %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -58,6 +58,10 @@
|
||||||
{% url 'settings-email-blocks' as url %}
|
{% url 'settings-email-blocks' as url %}
|
||||||
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Email Blocklist" %}</a>
|
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Email Blocklist" %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
{% url 'settings-ip-blocks' as url %}
|
||||||
|
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "IP Address Blocklist" %}</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.bookwyrm.edit_instance_settings %}
|
{% if perms.bookwyrm.edit_instance_settings %}
|
||||||
|
|
|
@ -156,6 +156,16 @@ urlpatterns = [
|
||||||
views.EmailBlocklist.as_view(),
|
views.EmailBlocklist.as_view(),
|
||||||
name="settings-email-blocks-delete",
|
name="settings-email-blocks-delete",
|
||||||
),
|
),
|
||||||
|
re_path(
|
||||||
|
r"^settings/ip-blocklist/?$",
|
||||||
|
views.IPBlocklist.as_view(),
|
||||||
|
name="settings-ip-blocks",
|
||||||
|
),
|
||||||
|
re_path(
|
||||||
|
r"^settings/ip-blocks/(?P<block_id>\d+)/delete/?$",
|
||||||
|
views.IPBlocklist.as_view(),
|
||||||
|
name="settings-ip-blocks-delete",
|
||||||
|
),
|
||||||
# moderation
|
# moderation
|
||||||
re_path(r"^settings/reports/?$", views.Reports.as_view(), name="settings-reports"),
|
re_path(r"^settings/reports/?$", views.Reports.as_view(), name="settings-reports"),
|
||||||
re_path(
|
re_path(
|
||||||
|
|
|
@ -5,6 +5,7 @@ from .admin.federation import Federation, FederatedServer
|
||||||
from .admin.federation import AddFederatedServer, ImportServerBlocklist
|
from .admin.federation import AddFederatedServer, ImportServerBlocklist
|
||||||
from .admin.federation import block_server, unblock_server
|
from .admin.federation import block_server, unblock_server
|
||||||
from .admin.email_blocklist import EmailBlocklist
|
from .admin.email_blocklist import EmailBlocklist
|
||||||
|
from .admin.ip_blocklist import IPBlocklist
|
||||||
from .admin.invite import ManageInvites, Invite, InviteRequest
|
from .admin.invite import ManageInvites, Invite, InviteRequest
|
||||||
from .admin.invite import ManageInviteRequests, ignore_invite_request
|
from .admin.invite import ManageInviteRequests, ignore_invite_request
|
||||||
from .admin.reports import (
|
from .admin.reports import (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
""" moderation via flagged posts and users """
|
""" Manage email blocklist"""
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
@ -14,7 +14,7 @@ from bookwyrm import forms, models
|
||||||
name="dispatch",
|
name="dispatch",
|
||||||
)
|
)
|
||||||
class EmailBlocklist(View):
|
class EmailBlocklist(View):
|
||||||
"""Block users by email address"""
|
"""Block registration by email address"""
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""view and compose blocks"""
|
"""view and compose blocks"""
|
||||||
|
|
49
bookwyrm/views/admin/ip_blocklist.py
Normal file
49
bookwyrm/views/admin/ip_blocklist.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
""" Manage IP blocklist """
|
||||||
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
|
from bookwyrm import forms, models
|
||||||
|
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@method_decorator(
|
||||||
|
permission_required("bookwyrm.moderate_user", raise_exception=True),
|
||||||
|
name="dispatch",
|
||||||
|
)
|
||||||
|
class IPBlocklist(View):
|
||||||
|
"""Block registration by ip address"""
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
"""view and compose blocks"""
|
||||||
|
data = {
|
||||||
|
"addresses": models.IPBlocklist.objects.all(),
|
||||||
|
"form": forms.IPBlocklistForm(),
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, "settings/ip_blocklist.html", data)
|
||||||
|
|
||||||
|
def post(self, request, block_id=None):
|
||||||
|
"""create a new ip address block"""
|
||||||
|
if block_id:
|
||||||
|
return self.delete(request, block_id)
|
||||||
|
|
||||||
|
form = forms.IPBlocklistForm(request.POST)
|
||||||
|
data = {
|
||||||
|
"addresses": models.IPBlocklist.objects.all(),
|
||||||
|
"form": form,
|
||||||
|
}
|
||||||
|
if not form.is_valid():
|
||||||
|
return TemplateResponse(request, "settings/ip_blocklist.html", data)
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
data["form"] = forms.IPBlocklistForm()
|
||||||
|
return TemplateResponse(request, "settings/ip_blocklist.html", data)
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def delete(self, request, domain_id):
|
||||||
|
"""remove a domain block"""
|
||||||
|
domain = get_object_or_404(models.IPBlocklist, id=domain_id)
|
||||||
|
domain.delete()
|
||||||
|
return redirect("settings-ip-blocks")
|
Loading…
Reference in a new issue