From 6df398966e832ca8f54034921a7ad13d9407d781 Mon Sep 17 00:00:00 2001 From: iou1name Date: Tue, 1 Oct 2019 23:06:51 -0400 Subject: [PATCH] first commit --- .gitignore | 4 +++ LICENSE | 15 +++++++++++ README.md | 24 +++++++++++++++++ buckler_aiohttp.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ config.py.template | 25 ++++++++++++++++++ saddle.py | 45 ++++++++++++++++++++++++++++++++ saddle.sql | 7 +++++ static/saddle.css | 7 +++++ templates/index.html | 16 ++++++++++++ 9 files changed, 204 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 buckler_aiohttp.py create mode 100755 config.py.template create mode 100644 saddle.py create mode 100644 saddle.sql create mode 100644 static/saddle.css create mode 100644 templates/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..610faed --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.swp +*.swo +config.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..410723e --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2019, iou1name + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1496de8 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Saddle +A file hosting service similar to Pomf and Uguu but without the public nature. + +## Requirements +Python 3.7+ +PostgreSQL 11.5+ +Python packages: `wheel gunicorn aiohttp aiohttp_jinja2 asyncpg uvloop` + +## Install +``` +$ psql +postgres=# CREATE DATABASE "saddle"; +postgres=# CREATE USER "saddle" WITH PASSWORD 'password'; +postgres=# ALTER ROLE "saddle" SET client_encoding TO 'utf8'; +postgres=# ALTER ROLE "saddle" SET default_transaction_isolation TO 'read committed'; +postgres=# ALTER ROLE "saddle" SET timezone TO 'UTC'; +postgres=# GRANT ALL PRIVILEGES ON DATABASE "saddle" TO "saddle"; +postgres=# \q +``` +1. Get on the floor +2. Walk the dinosaur + +## Usage +`gunicorn saddle:init_app --bind localhost:5000 --worker-class aiohttp.GunicornWebWorker` diff --git a/buckler_aiohttp.py b/buckler_aiohttp.py new file mode 100644 index 0000000..a2c8ed6 --- /dev/null +++ b/buckler_aiohttp.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +""" +Session interface middlewares to integrate the aiohttp app with Buckler. +""" +import json +from datetime import datetime + +import aiohttp +from aiohttp import web + +import config + +@web.middleware +async def buckler_session(request, handler): + """ + Verifies the user with the configured Buckler app and retrieves any + session data they may have. Redirects them to the login page otherwise. + """ + user_id = request.cookies.get('userid') + user_sid = request.cookies.get('session') + + url = config.buckler['url'] + '/get_session' + params = { + 'app_id': config.buckler['app_id'], + 'app_key': config.buckler['app_key'], + 'userid': user_id, + 'session': user_sid + } + async with aiohttp.ClientSession() as session: + async with session.get(url, params=params) as resp: + data = await resp.json() + if data.get('error'): + raise web.HTTPFound(location=config.buckler['login_url']) + request['session'] = data['session_data'] + request['meta'] = data['meta'] + + resp = await handler(request) + + if request['session'] != data['session_data']: # session data modified + url = config.buckler['url'] + '/set_session' + data = json.dumps(request['session']) + session.post(url, params=params, data=data) # TODO: error handle? + + last_used = datetime.fromisoformat(request['meta']['last_used']) + now = datetime.now(last_used.tzinfo) + delta = now - last_used + if delta.seconds > 600: + resp.set_cookie( + 'userid', + user_id, + max_age=30*24*60*60, + secure=True, + httponly=True) + resp.set_cookie( + 'session', + user_sid, + max_age=30*24*60*60, + secure=True, + httponly=True) + + return resp diff --git a/config.py.template b/config.py.template new file mode 100755 index 0000000..5cd22dc --- /dev/null +++ b/config.py.template @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +""" +Configuration settings for Saddle. +`url_prefix` is the root path you wish app to reside at +eg. https://example.com/saddle +`upload_dir` is the system directory in which to store uploaded files. +`db` specifies parameters for connecting to the PostgreSQL database. +`buckler` specifies settings pertaining to the Buckler server. +""" +url_prefix = '/saddle' +upload_dir = '/var/www/html/up' +db = { + 'database': 'saddle', + 'user': 'saddle', + 'password': """password""", + 'host': 'localhost', + 'port': 5432, +} + +buckler = { + 'url': "http://127.0.0.1:5400/buckler", + 'app_id': 1, + 'app_key': """password""", + 'login_url': "/buckler/login", +} diff --git a/saddle.py b/saddle.py new file mode 100644 index 0000000..516c56c --- /dev/null +++ b/saddle.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +A file hosting service similar to Pomf and Uguu but without the public nature. +""" +from aiohttp import web +import jinja2 +import aiohttp_jinja2 +from aiohttp_jinja2 import render_template +import asyncpg +import uvloop + +import config +import buckler_aiohttp + +uvloop.install() +routes = web.RouteTableDef() + + +@routes.get('/', name='index') +async def index(request): + """The index page.""" + return render_template("index.html", request, locals()) + + +async def init_app(): + """Initializes the application.""" + #app = web.Application(middlewares=[buckler_aiohttp.buckler_session]) + app = web.Application() + aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('templates')) + app['pool'] = await asyncpg.create_pool(**config.db) + + async with app['pool'].acquire() as conn: + with open('saddle.sql', 'r') as file: + await conn.execute(file.read()) + + app.router.add_routes(routes) + + app_wrap = web.Application() + app_wrap.add_subapp(config.url_prefix, app) + return app_wrap + + +if __name__ == "__main__": + app = init_app() + web.run_app(app, host='0.0.0.0', port=5000) diff --git a/saddle.sql b/saddle.sql new file mode 100644 index 0000000..2e81596 --- /dev/null +++ b/saddle.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS upload ( + user_id INTEGER NOT NULL, + id TEXT PRIMARY KEY, + filename TEXT NOT NULL, + upload_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + expiration_date TIMESTAMP +) diff --git a/static/saddle.css b/static/saddle.css new file mode 100644 index 0000000..70e50c6 --- /dev/null +++ b/static/saddle.css @@ -0,0 +1,7 @@ +body { + margin-left: 10%; + margin-right: 10%; + background-color: lightcoral; + font-family: Helvetica,sans-serif; + font-size: 14px; +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..2b2c58c --- /dev/null +++ b/templates/index.html @@ -0,0 +1,16 @@ + + + + Saddle + + + + + +
+

Saddle

+
+
+
+ +