check personal user bans

This commit is contained in:
Izalia Mae 2020-01-17 07:32:40 -05:00
parent 69cb899a93
commit 3c3f49c110
7 changed files with 171 additions and 19 deletions

View file

@ -107,5 +107,5 @@ class LRUCache:
if key in self.items:
return self.items[key]
return None
return

View file

@ -53,9 +53,8 @@ else:
PAWSCONFIG = {
'host': env.get('PAWS_HOST', '127.0.0.1'),
'port': int(env.get('PAWS_PORT', 3001)),
'user': env.get('PAWS_USER', 'admin'),
'pass': env.get('PAWS_PASS', 'password'),
'mastopath': env.get('MASTOPATH', os.getcwd())
'admin': env.get('PAWS_ADMIN', None),
'mastopath': env.get('MASTOPATH', os.getcwd()),
}

View file

@ -1,16 +1,19 @@
import sys
from DBUtils.PooledPg import PooledPg as DB
from datetime import datetime
from urllib.parse import urlparse
from json.decoder import JSONDecodeError
from DBUtils.PooledPg import PooledPg as DB
from tinydb import TinyDB, Query, where
from tinydb_smartcache import SmartCacheTable
from tinyrecord import transaction as trans
from tldextract import extract
from urllib.parse import urlparse
from json.decoder import JSONDecodeError
from Crypto.PublicKey import RSA
from .config import stor_path, logging, MASTOCONFIG as mdb
from .functions import bool_check
from .cache import LRUCache
def jsondb():
@ -24,7 +27,7 @@ def jsondb():
db.table_class = SmartCacheTable
class table:
bans = db.table('bans')
keys = db.table('keys')
follows = db.table('follows')
users = db.table('users')
@ -44,6 +47,34 @@ def pgdb():
sys.exit()
def keys(actor):
if pawsdb.keys.get(query.actor == actor) == None:
logging.info(f'No RSA key. Generating one for {actor}...')
PRIV = RSA.generate(4096)
PUB = PRIV.publickey()
keydata = {
'actor': actor,
'pubkey': PUB.exportKey('PEM').decode('utf-8'),
'privkey': PRIV.exportKey('PEM').decode('utf-8')
}
with trans(pawsdb.keys) as tr:
tr.insert(keydata)
return pawsdb.keys.get(query.actor == actor)
def get_handle(userid):
user_data = mastodb.query(f'SELECT * FROM public.accounts WHERE id = \'{userid}\'').dictresult()
if len(user_data) < 1:
return
return (user_data[0]['username'].lower(), user_data[0]['domain'].lower())
def get_bans(suspend=True, details=False):
domains = mastodb.query('SELECT * FROM public.domain_blocks;').dictresult()
banlist = {} if details else []
@ -84,6 +115,32 @@ def ban_check(url):
logging.debug(f'{parsed} not in blocklist')
def user_ban_check(user, access_user):
user_data = mastodb.query(f'SELECT * FROM public.accounts WHERE username = \'{user}\'').dictresult()
if len(user_data) < 1:
return
userid = user_data[0]['id']
userban_query = mastodb.query(f'SELECT * FROM public.blocks WHERE account_id = \'{userid}\'').dictresult()
userbans = []
for ban in userban_query:
acct_id = ban['target_account_id']
user = get_handle(acct_id)
if user:
userbans.append(user)
else:
logging.warning(f'Invalid userid: {acct_id}')
if access_user in userbans:
return True
return False
pawsdb = jsondb()
query = Query()
mastodb = pgdb()

View file

@ -3,6 +3,8 @@ import re
import json
import logging
from http import client as http
from colour import Color
from aiohttp_jinja2 import render_template as render
@ -46,6 +48,27 @@ def error(code, error):
raise eval('aiohttp.web.HTTP'+error_codes[code]+'(body=error_body, content_type=cont_type)')
def fed_domain(user, domain):
headers = {'Accept': 'application/json'}
conn = http.HTTPSConnection(domain)
conn.request('GET', f'/.well-known/webfinger?resource=acct:{user}@{domain}', headers=headers)
response = conn.getresponse()
if response.status != 200:
return
try:
data = json.loads(response.read())
except:
logging.debug(f'Invalid user: {user}@{domain}')
wf_data = data.get('subject')
user_data = wf_data.replace('acct:', '').split('@')
return user_data[1]
def user_check(path):
parsed = path.replace('.json', '').split('/')

View file

@ -7,13 +7,14 @@ import base64
import traceback
from urllib.parse import urlparse, quote_plus
from aiohttp_jinja2 import render_template as render
from aiohttp.http_exceptions import *
from aiohttp.client_exceptions import *
from .signature import validate, pass_hash
from .functions import error, user_check
from .config import MASTOCONFIG, PAWSCONFIG, VERSION, script_path
from .database import pawsdb, query, trans, ban_check
from .database import pawsdb, query, trans, ban_check, user_ban_check
# I'm a little teapot :3
@ -94,7 +95,10 @@ async def passthrough(path, headers, post=None, query=None):
querydata = query if query else ''
try:
async with aiohttp.request(reqtype, f'https://{MASTOCONFIG["domain"]}/{path}{query}', headers=headers, data=post) as resp:
# I don't think I need this, but I'll leave it here just in case
#async with aiohttp.request(reqtype, f'https://{MASTOCONFIG["domain"]}/{path}{query}', headers=headers, data=post) as resp:
async with aiohttp.request(reqtype, f'http://localhost:3000/{path}{query}', headers=headers, data=post) as resp:
data = await resp.read()
if resp.status not in [200, 202]:
@ -163,9 +167,16 @@ async def http_signatures(app, handler):
request['reqtype'] = 'html'
token = request.cookies.get('paws_token')
access_user = pawsdb.users.get(query.token == token)
if not token or not pawsdb.users.get(query.token == token):
return aiohttp.web.HTTPFound(f'/paws/login?redir={quote_plus(request.path)}')
if not token or not access_user:
return aiohttp.web.HTTPFound(f'/paws/login?msg=InvalidToken&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, (access_user['handle'].lower(), access_user['instance'])):
return render('pages/error.html', request, {'msg': 'Access Denied', 'code': '403'}, status=403)
return (await handler(request))
return http_signatures_handler
@ -208,7 +219,7 @@ async def http_trailing_slash(app, handler):
async def http_server_header(request, response):
response.headers['SErver'] = f'PAWS/{VERSION}'
response.headers['Server'] = f'PAWS/{VERSION}'
response.headers['trans_rights'] = 'are human rights'

View file

@ -25,11 +25,15 @@ def webserver():
web.on_response_prepare.append(middleware.http_server_header)
web.add_routes([
aiohttp.web.route('GET', '/paws', views.get_paws),
aiohttp.web.route('POST', '/paws', 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.css', views.get_style),
aiohttp.web.route('POST', '/paws/inbox', views.post_inbox),
aiohttp.web.route('GET', '/paws/actor', views.get_actor)
])
async def global_vars(request):

View file

@ -4,16 +4,21 @@ import random
from aiohttp_jinja2 import render_template as render
from urllib.parse import quote_plus, unquote_plus
from .database import pawsdb, trans, query, where
from .functions import error
from .database import pawsdb, trans, query, where, keys
from .functions import error, fed_domain
from .oauth import create_app, login
from .config import MASTOCONFIG
async def get_login(request):
parms = request.rel_url.query
redir = parms.get('redir')
token = request.cookies.get('paws_token')
numid = random.randint(1*1000000, 10*1000000-1)
if pawsdb.users.get(query.token == token):
return aiohttp.web.HTTPFound(f'/about')
return render('pages/login.html', request, {'redir': redir, 'numid': numid})
@ -26,13 +31,15 @@ async def post_login(request):
if domain in ['', None]:
return render('pages/login.html', request, {'msg': 'Missing domain'})
if ban_check(domain):
return render('pages/error.html', request, {'msg': 'Instance banned', 'code': '403'}, status=403)
appid, appsecret, redir_url = create_app(domain)
with trans(pawsdb.users) as tr:
tr.insert({
'handle': data['numid'],
'domain': data['domain'],
'icon': None,
'appid': appid,
'appsecret': appsecret,
'token': None
@ -55,13 +62,17 @@ async def get_auth(request):
code = parms.get('code')
if None in [numid, code]:
response = render('pages/error.html', request, {'msg': 'Missing temporary userid or auth code', 'code': 500}, status=500)
return render('pages/error.html', request, {'msg': 'Missing temporary userid or auth code', 'code': '500'}, status=500)
if redir in ['', None]:
redir = '/about'
user = pawsdb.users.get(query.handle == str(numid))
token, userinfo = login(user, code)
instance = fed_domain(userinfo['username'], user['domain'])
with trans(pawsdb.users) as tr:
tr.update({'handle': userinfo['username'], 'icon': userinfo['avatar_static'], 'token': token}, where('handle') == numid)
tr.update({'handle': userinfo['username'], 'instance': instance, 'token': token}, where('handle') == numid)
print(user)
@ -90,3 +101,50 @@ async def get_style(request):
return response
async def get_actor(request):
rsakey = keys('default')
HOST = MASTOCONFIG['domain']
if not rsakey:
render('pages/error.html', request, {'msg': 'Missing actor keys', 'code': 500}, status=500)
PUBKEY = rsakey['pubkey']
data = {
'@context': [
'https://www.w3.org/ns/activitystreams'
],
'endpoints': {
'sharedInbox': f'https://{HOST}/inbox'
},
'inbox': f'https://{HOST}/paws/inbox',
'name': f'PAWS @ {HOST}',
'type': 'Application',
'id': f'https://{HOST}/paws/actor',
'publicKey': {
'id': f'https://{HOST}/paws/actor#main-key',
'owner': f'https://{HOST}/paws/actor',
'publicKeyPem': PUBKEY
},
'summary': 'PAWS Actor',
'preferredUsername': 'paws',
'url': f'https://{HOST}/paws/actor'
}
return aiohttp.web.json_response(data)
async def post_inbox(request):
return aiohttp.web.json_response('UvU', status=202)
async def get_paws(request):
return aiohttp.web.json_response('UvU', status=202)
async def post_paws(request):
return aiohttp.web.json_response('UvU', status=202)