honor user domain blocks and many changes
This commit is contained in:
parent
545a68eeb1
commit
22fe37d38a
|
@ -72,3 +72,6 @@ While it isn't necessary, I highly recommend turning on authorized fetches (v3.0
|
|||
AUTHORIZED_FETCH=true
|
||||
```
|
||||
|
||||
## WebUI usage
|
||||
|
||||
If your account is an admin account according to Mastodon, you can manage the PAWS whitelist at {domain}/paws. Instances in this whitelist will have their fetches signed if they don't sign them themselves.
|
||||
|
|
|
@ -167,8 +167,21 @@ def user_ban_check(user, access_user):
|
|||
return False
|
||||
|
||||
|
||||
def user_domain_ban_check(handle, access_domain):
|
||||
user_data = get_user(handle)
|
||||
|
||||
if not user_data:
|
||||
return
|
||||
|
||||
userid = user_data['id']
|
||||
ban_data = mastodb.query(f'SELECT * FROM public.account_domain_blocks WHERE account_id = \'{userid}\' and domain = \'{access_domain}\'').dictresult()
|
||||
|
||||
return ban_data
|
||||
|
||||
|
||||
def wl_check(domain):
|
||||
return pawsdb.whitelist.get(query.domain == domain)
|
||||
data = pawsdb.whitelist.get(query.domain == domain)
|
||||
return data
|
||||
|
||||
|
||||
def admin_check(handle):
|
||||
|
@ -206,4 +219,3 @@ def whitelist(action, instance):
|
|||
pawsdb = jsondb()
|
||||
query = Query()
|
||||
mastodb = pgdb()
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import re
|
|||
import json
|
||||
import logging
|
||||
import socket
|
||||
import os
|
||||
|
||||
import aiohttp
|
||||
import validators
|
||||
|
@ -132,6 +133,17 @@ def dig(domain):
|
|||
logging.info(f'Failed to resolve IP: {e}')
|
||||
|
||||
|
||||
def css_ts():
|
||||
from .config import script_path
|
||||
|
||||
css_check = lambda css_file : int(os.path.getmtime(f'{script_path}/templates/{css_file}.css'))
|
||||
|
||||
color = css_check('color')
|
||||
layout = css_check('layout')
|
||||
|
||||
return color + layout
|
||||
|
||||
|
||||
class color:
|
||||
def __init__(self):
|
||||
self.check = lambda color: Color(f'#{str(color)}' if re.search(r'^(?:[0-9a-fA-F]{3}){1,2}$', color) else color)
|
||||
|
|
|
@ -18,7 +18,11 @@ from Crypto.PublicKey import RSA
|
|||
from .signature import validate, pass_hash, sign_headers
|
||||
from .functions import error, user_check, domain_check, parse_sig, parse_ua, dig
|
||||
from .config import MASTOCONFIG, PAWSCONFIG, VERSION, script_path
|
||||
from .database import pawsdb, query, trans, ban_check, user_ban_check, banned_user_check, wl_check, keys
|
||||
from .database import pawsdb, query, trans, ban_check, user_ban_check, banned_user_check, wl_check, keys, user_domain_ban_check
|
||||
|
||||
|
||||
paws_host = PAWSCONFIG['domain']
|
||||
masto_host = MASTOCONFIG['domain']
|
||||
|
||||
|
||||
# I'm a little teapot :3
|
||||
|
@ -103,7 +107,7 @@ async def http_filter(app, handler):
|
|||
logging.info(f'Blocked user: {domain}')
|
||||
return http_error(request, 403, 'Access Denied')
|
||||
|
||||
if any(map(request.path.startswith, auth_paths)):
|
||||
if any(map(request.path.startswith, auth_paths)) and request.method == 'GET':
|
||||
if 'json' in request.headers.get('Accept', '') or request.path.endswith('.json'):
|
||||
request['reqtype'] = 'json'
|
||||
|
||||
|
@ -125,16 +129,15 @@ async def http_filter(app, handler):
|
|||
raise error(401, msg)
|
||||
|
||||
else:
|
||||
logging.error('heck')
|
||||
request['reqtype'] = 'html'
|
||||
|
||||
if not token or not user_data:
|
||||
return aiohttp.web.HTTPFound(f'/paws/login?redir={quote_plus(request.path)}')
|
||||
return aiohttp.web.HTTPFound(f'https://{masto_host}/paws/login?redir={quote_plus(request.path)}')
|
||||
|
||||
split_path = request.path.split('/')
|
||||
user = split_path[1].replace('@', '') if request.path.startswith('/@') else split_path[2]
|
||||
|
||||
if user_ban_check(user.lower(), (user_data['handle'].lower(), user_data['instance'])):
|
||||
if user_ban_check(user.lower(), (user_data['handle'].lower(), user_data['instance'])) or user_domain_ban_check(user.lower(), user_data['instance']):
|
||||
return http_error(request, 403, 'Access Denied')
|
||||
|
||||
querydata = request.query
|
||||
|
@ -150,18 +153,14 @@ async def http_filter(app, handler):
|
|||
|
||||
urlquery = rawquery if rawquery != '?' else None
|
||||
|
||||
# This is supposed to sign whitelisted unsigned GETs, but it didn't work
|
||||
if not signature and real_ip == ua_ip and wl_check(domain) and request.method == 'GET':
|
||||
logging.info(f'Signing fetch for whitelisted instance: {domain}')
|
||||
|
||||
host = MASTOCONFIG['domain']
|
||||
paws_host = PAWSCONFIG['domain']
|
||||
|
||||
HEADERS = {
|
||||
'(request-target)': f'get {request.path}',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': f'PAWS/{VERSION}; https://{host}',
|
||||
'Host': host,
|
||||
'User-Agent': f'PAWS/{VERSION}; https://{masto_host}',
|
||||
'Host': masto_host,
|
||||
'X-Forwarded-For': request.headers.get('X-Forwarded-For', request.remote),
|
||||
'X-Forwarded-Port': '443',
|
||||
'X-Forwarded-Proto': 'https',
|
||||
|
|
|
@ -9,7 +9,7 @@ from ipaddress import ip_address as address
|
|||
from urllib.parse import urlparse
|
||||
|
||||
from .config import PAWSCONFIG, MASTOCONFIG, VERSION, script_path, logging
|
||||
from .functions import color
|
||||
from .functions import color, css_ts
|
||||
from . import middleware
|
||||
|
||||
|
||||
|
@ -26,22 +26,27 @@ def webserver():
|
|||
web.add_routes([
|
||||
aiohttp.web.route('GET', '/', views.get_home),
|
||||
aiohttp.web.route('GET', '/paws', views.get_paws),
|
||||
aiohttp.web.route('GET', '/paws/imgay', views.get_gay),
|
||||
aiohttp.web.route('POST', '/paws/action/{action}', views.post_paws),
|
||||
aiohttp.web.route('GET', '/paws/login', views.get_login),
|
||||
aiohttp.web.route('POST', '/paws/login', views.post_login),
|
||||
aiohttp.web.route('GET', '/paws/logout', views.get_logout),
|
||||
aiohttp.web.route('GET', '/paws/auth', views.get_auth),
|
||||
aiohttp.web.route('GET', '/paws/style.css', views.get_style),
|
||||
aiohttp.web.route('GET', '/paws/style-{timestamp}.css', views.get_style),
|
||||
|
||||
aiohttp.web.route('GET', '/.well-known/webfinger', views.get_webfinger),
|
||||
aiohttp.web.route('POST', '/paws/inbox', views.post_inbox),
|
||||
aiohttp.web.route('GET', '/paws/actor', views.get_actor)
|
||||
aiohttp.web.route('GET', '/paws/actor', views.get_actor),
|
||||
|
||||
#aiohttp.web.route('GET', '/paws/outbox', views.get_outbox),
|
||||
#aiohttp.web.route('GET', '/paws/imgay', views.get_gay)
|
||||
])
|
||||
|
||||
async def global_vars(request):
|
||||
return {
|
||||
'VERSION': VERSION,
|
||||
'len': len,
|
||||
'css_ts': css_ts,
|
||||
'request': request,
|
||||
'domain': MASTOCONFIG['domain'],
|
||||
'urlparse': urlparse,
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>PAWS@{{domain}}: {{page}}</title>
|
||||
<link rel=stylesheet href="/paws/style.css" media="screen">
|
||||
<link rel=stylesheet href="/paws/style-{{css_ts()}}.css" media="screen">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
|
@ -22,7 +23,7 @@
|
|||
</div>
|
||||
<div class="grid-item col2">
|
||||
{% if request.user %}
|
||||
<p>{{request.user.handle}} [<a href="/paws/logout">logout</a>]</p>
|
||||
<p>{{request.user.handle}}@{{request.user.instance}} [<a href="/paws/logout">logout</a>]</p>
|
||||
{% else %}
|
||||
<p>Guest [<a href="/paws/login">login</a>]</p>
|
||||
{% endif %}
|
||||
|
|
|
@ -36,7 +36,6 @@ summary:hover {
|
|||
#header h1 {
|
||||
margin: 0px;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -146,7 +145,7 @@ tr:last-child .col2 {
|
|||
min-width: 200px;
|
||||
}
|
||||
|
||||
#auth input[type=text]:hover{
|
||||
#auth input[type=text]:hover {
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
|
@ -160,6 +159,11 @@ tr:last-child .col2 {
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
#whitelist .col2 {
|
||||
text-align: center;
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
|
||||
/* Errors */
|
||||
#error .msg {
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
<div class="section" id="auth">
|
||||
<h2 class="title">PAWS</h2>
|
||||
|
||||
UvU
|
||||
PAWS is an extra auth layer for public profiles and posts. The main purpose is to prevent web scrapers from hoarding posts, but it also prevents banned users from viewing toots they shouldn't see.
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% set page = 'Control Panel' %}
|
||||
{% set page = 'Home' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="section" id="auth">
|
||||
<h2 class="title">Control Panel</h2>
|
||||
|
||||
UvU<br /><br />
|
||||
|
||||
<div class="section" id="panel">
|
||||
<h2 class="title">PAWS</h2>
|
||||
PAWS is an extra auth layer for public profiles and posts. The main purpose is to prevent web scrapers from hoarding posts, but it also prevents banned users from viewing toots they shouldn't see.<br /><br />
|
||||
{% if admin %}
|
||||
|
||||
<center><h2>Whitelist</h2></center>
|
||||
<table id="whitelist">
|
||||
<tr class="header">
|
||||
<td class="col1">Instance</td>
|
||||
<td>Remove</td>
|
||||
</tr>
|
||||
<tr class="header"><td></td><td></td></tr>
|
||||
{% if len(whitelist) > 0 %}
|
||||
{% for instance in whitelist %}
|
||||
<tr class="instance">
|
||||
<td class="col1"><a href="https://{{instance}}/about">{{instance}}</a></td>
|
||||
<td>
|
||||
<td class="col1"><a href="https://{{instance}}/about" target="_new">{{instance}}</a></td>
|
||||
<td class="col2">
|
||||
<form action="https://{{domain}}/paws/action/remove" method="post">
|
||||
<input name="name" value="{{instance}}" hidden>
|
||||
<input type="submit" value="X">
|
||||
|
@ -32,7 +29,7 @@
|
|||
<tr class="instance">
|
||||
<form action="https://{{domain}}/paws/action/add" method="post">
|
||||
<td class="col1"><input type="text" name="name" placeholder="bofa.lol"></td>
|
||||
<td>
|
||||
<td class="col2">
|
||||
<input type="submit" value="Add">
|
||||
</td>
|
||||
</form>
|
||||
|
|
106
paws/views.py
106
paws/views.py
|
@ -5,14 +5,19 @@ import logging
|
|||
|
||||
from aiohttp_jinja2 import render_template as render
|
||||
from urllib.parse import quote_plus, unquote_plus, urlparse
|
||||
from datetime import datetime
|
||||
|
||||
from .database import pawsdb, trans, query, where, keys, ban_check, get_user, get_toot, admin_check, whitelist
|
||||
from .functions import error, http_error, fed_domain, domain_check
|
||||
from .functions import error, http_error, fed_domain, domain_check, css_ts
|
||||
from .oauth import create_app, login
|
||||
from .config import MASTOCONFIG, PAWSCONFIG
|
||||
from .cache import TTLCache
|
||||
|
||||
|
||||
paws_host = PAWSCONFIG['domain']
|
||||
masto_host = MASTOCONFIG['domain']
|
||||
|
||||
|
||||
async def get_home(request):
|
||||
'heck2'
|
||||
|
||||
|
@ -27,6 +32,7 @@ async def get_paws(request):
|
|||
|
||||
if admin:
|
||||
whitelist = [line['domain'] for line in pawsdb.whitelist.all()]
|
||||
whitelist.sort()
|
||||
|
||||
else:
|
||||
whitelist = None
|
||||
|
@ -57,7 +63,7 @@ async def post_paws(request):
|
|||
|
||||
whitelist(action, parsed_domain)
|
||||
|
||||
return aiohttp.web.HTTPFound('/paws')
|
||||
return aiohttp.web.HTTPFound(f'https://{masto_host}/paws')
|
||||
|
||||
|
||||
async def get_login(request):
|
||||
|
@ -67,7 +73,7 @@ async def get_login(request):
|
|||
numid = random.randint(1*1000000, 10*1000000-1)
|
||||
|
||||
if pawsdb.users.get(query.token == token):
|
||||
return aiohttp.web.HTTPFound(f'/about')
|
||||
return aiohttp.web.HTTPFound(f'https://{masto_host}/paws')
|
||||
|
||||
return render('pages/login.html', request, {'redir': redir, 'numid': numid})
|
||||
|
||||
|
@ -157,37 +163,40 @@ async def get_logout(request):
|
|||
|
||||
|
||||
async def get_style(request):
|
||||
maxage = 60*60*24*7
|
||||
|
||||
response = render('color.css', request, {})
|
||||
response.headers['Content-Type'] = 'text/css'
|
||||
response.headers['Content-type'] = 'text/css'
|
||||
#response.headers['Last-Modified'] = datetime.utcfromtimestamp(css_ts()).strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
response.headers['Cache-Control'] = 'public,max-age={maxage},immutable'
|
||||
|
||||
return response
|
||||
|
||||
|
||||
async def get_webfinger(request):
|
||||
domain = PAWSCONFIG['domain']
|
||||
res = request.rel_url.query.get('resource')
|
||||
|
||||
if not res or res != f'acct:paws@{domain}':
|
||||
if not res or res != f'acct:paws@{paws_host}':
|
||||
data = {}
|
||||
|
||||
else:
|
||||
data = {
|
||||
"aliases": [
|
||||
f"https://{domain}/paws/actor"
|
||||
f"https://{paws_host}/paws/actor"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"href": f"https://{domain}/paws/actor",
|
||||
"href": f"https://{paws_host}/paws/actor",
|
||||
"rel": "self",
|
||||
"type": "application/activity+json"
|
||||
},
|
||||
{
|
||||
"href": f"https://{domain}/paws/actor",
|
||||
"href": f"https://{paws_host}/paws/actor",
|
||||
"rel": "self",
|
||||
"type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
||||
}
|
||||
],
|
||||
"subject": f"acct:paws@{domain}"
|
||||
"subject": f"acct:paws@{paws_host}"
|
||||
}
|
||||
|
||||
return aiohttp.web.json_response(data)
|
||||
|
@ -195,8 +204,6 @@ async def get_webfinger(request):
|
|||
|
||||
async def get_actor(request):
|
||||
rsakey = keys('default')
|
||||
HOST = MASTOCONFIG['domain']
|
||||
host = PAWSCONFIG['domain']
|
||||
|
||||
if not rsakey:
|
||||
return http_error(request, 500, 'Missing actor keys')
|
||||
|
@ -208,22 +215,23 @@ async def get_actor(request):
|
|||
'https://www.w3.org/ns/activitystreams'
|
||||
],
|
||||
#'endpoints': {
|
||||
#'sharedInbox': f'https://{HOST}/inbox'
|
||||
#'sharedInbox': f'https://{masto_host}/inbox'
|
||||
#},
|
||||
#'following': f'https://{host}/paws/following',
|
||||
#'followers': f'https://{host}/paws/followers',
|
||||
'inbox': f'https://{host}/paws/inbox',
|
||||
'name': f'PAWS @ {HOST}',
|
||||
'inbox': f'https://{paws_host}/paws/inbox',
|
||||
#'outbox': f'https://{host}/paws/outbox',
|
||||
'name': f'PAWS @ {masto_host}',
|
||||
'type': 'Application',
|
||||
'id': f'https://{host}/paws/actor',
|
||||
'id': f'https://{paws_host}/paws/actor',
|
||||
'publicKey': {
|
||||
'id': f'https://{host}/paws/actor#main-key',
|
||||
'owner': f'https://{host}/paws/actor',
|
||||
'id': f'https://{paws_host}/paws/actor#main-key',
|
||||
'owner': f'https://{paws_host}/paws/actor',
|
||||
'publicKeyPem': PUBKEY
|
||||
},
|
||||
'summary': 'PAWS Actor',
|
||||
'preferredUsername': 'paws',
|
||||
'url': f'https://{host}/paws/actor'
|
||||
'url': f'https://{paws_host}/paws/actor'
|
||||
}
|
||||
|
||||
return aiohttp.web.json_response(data)
|
||||
|
@ -232,3 +240,63 @@ async def get_actor(request):
|
|||
async def post_inbox(request):
|
||||
return aiohttp.web.json_response('UvU', status=202)
|
||||
|
||||
|
||||
# failed attempt at creating a status
|
||||
async def get_outbox(request):
|
||||
if not request.rel_url.query.get('page'):
|
||||
data = {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": f"https://{paws_host}/paws/outbox",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 1,
|
||||
"first": f"https://{paws_host}/paws/outbox?page=true",
|
||||
"last": f"https://{paws_host}/paws/outbox?page=true"
|
||||
}
|
||||
|
||||
else:
|
||||
data = {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount",
|
||||
"blurhash": "toot:blurhash",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"Emoji": "toot:Emoji"
|
||||
}
|
||||
],
|
||||
"id": f"https://{paws_host}/paws/outbox?page=true",
|
||||
"type": "OrderedCollectionPage",
|
||||
"partOf": f"https://{paws_host}/paws/outbox",
|
||||
"orderedItems": [imgay]
|
||||
}
|
||||
|
||||
return aiohttp.web.json_response(data)
|
||||
|
||||
async def get_gay(request):
|
||||
data = imgay
|
||||
|
||||
return aiohttp.web.json_response(data)
|
||||
|
||||
|
||||
imgay = {
|
||||
"id": f"https://{paws_host}/paws/imgay",
|
||||
"type": "Note",
|
||||
"published": '2020-01-20T09:45:00Z',
|
||||
"attributedTo": f"https://{paws_host}/paws/actor",
|
||||
"content": '<p>im gay</p>',
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
f"https://{paws_host}/paws/actor/followers"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue