initial commit

This commit is contained in:
nihonium 2023-01-15 13:53:21 +03:00
commit 3b3c9a9417
Signed by: nihonium
GPG key ID: 0251623741027CFC
258 changed files with 20086 additions and 0 deletions

View file

@ -0,0 +1,17 @@
FROM python:3-slim
RUN useradd -ms /bin/bash volgactf
RUN pip install --no-cache-dir quart nanoid requests flask_sqlalchemy aiohttp psycopg2-binary quart-cors
WORKDIR /home/volgactf/dist
COPY . ./
RUN chown -R volgactf:volgactf /home/volgactf/dist
USER volgactf
CMD python routes.py

View file

@ -0,0 +1,12 @@
from quart import Quart
from flask_sqlalchemy import SQLAlchemy
import os, re
from quart_cors import cors, route_cors
app = Quart(__name__)
app = cors(app, allow_origin=re.compile(r"http:\/\/.*:3000"), allow_credentials=True)#cors(app, allow_origin="http://10.50.20.4:3000", allow_credentials=True)
app.config["REDIRECT_SERVER"] = os.getenv('REDIRECT_SERVER') or "127.0.0.1:8080"#"10.50.20.7:8080"
app.config["CONTENT_SERVER"] = os.getenv('CONTENT_SERVER') or "127.0.0.1:13379"#"10.50.20.6:13379"
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv('SQLALCHEMY_DATABASE_URI') or 'sqlite:///data.db'
app.config["SECRET_KEY"] = 'hack_me'
db = SQLAlchemy(app)

View file

@ -0,0 +1,45 @@
from werkzeug.security import generate_password_hash, check_password_hash
from nanoid import generate
from . import db
class User(db.Model):
def __init__(self, username, blog_url):
self.username = username
self.blog_url = blog_url
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.Text, unique=True)
password = db.Column(db.Text)
blog_url = db.Column(db.Text, db.ForeignKey('blogs.url', ondelete='CASCADE'), unique=True)
blog = db.relationship('Blog', backref='blog_user', cascade="all,delete")
def set_password(self, password):
self.password = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password, password)
class Blog(db.Model):
def __init__(self, url, is_private=False):
self.url = url
self.is_private = is_private
__tablename__ = "blogs"
id = db.Column(db.Integer, primary_key=True)
url = db.Column(db.Text, unique=True)
is_private = db.Column(db.Boolean, default=False) #TODO this is must be private (VULN)
user = db.relationship('User', backref='blog_user', cascade="all,delete", lazy='dynamic')
posts = db.relationship("Post", cascade="all,delete")
#TODO дописать стуруктуру БД
class Post(db.Model):
def __init__(self, title, body, blog_url):
self.title = title
self.body = body
self.blog_url = blog_url
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.String)
blog_url = db.Column(db.String, db.ForeignKey("blogs.url"))
blog = db.relationship("Blog", cascade="all,delete")

View file

@ -0,0 +1,11 @@
from app import app, db
from flask import g, current_app
from flask_sqlalchemy import SQLAlchemy
if __name__ == "__main__":
def get_db():
if 'db' not in g:
g.db = SQLAlchemy(current_app)
return g.db
get_db().create_all()
#db.create_all()

View file

@ -0,0 +1,255 @@
from quart import request, Response, render_template, session, redirect, jsonify, abort, current_app, url_for
import aiohttp
from app import app, db
from app.models import User, Blog, Post
from nanoid import generate
from functools import wraps
import json
from quart_cors import route_cors
@app.before_first_request
def create_tables():
db.create_all()
def user_cook(f):
@wraps(f)
async def decorated_function(*args, **kwargs):
if session.get('username'):
return await current_app.ensure_async(f)(*args, **kwargs)
else:
return abort(403)
return decorated_function
@app.route('/health_check')
#@route_cors()
async def health_check():
status1, _, _, _ = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/health_check")
status2, _, _, _ = await get_request(f"http://{app.config.get('CONTENT_SERVER')}/health_check")
if status1 == 200 and status2 == 200:
return "OK"
else:
return "Not OK", 500
@app.route('/health_check2')
#@route_cors()
async def health_check2():
return "OK"
@app.route('/image/<path>')
#@route_cors()
async def get_image(path): # put application's code here
#if x.get(""): #if valid image (example 404, 405, corrupted_image)
#...
#else:
path2 = request.args.get("another") #путь до картинки (логика хранения картинки как в CDN (начало пути - начало хеша).
filename = request.args.get("filename")
content_server = app.config.get("CONTENT_SERVER")
if path2 == 'secrets' or path == 'secrets':
return abort(403)
status_code, json_data, data_data, headers = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path}/{content_server}?filename={filename}")
if status_code == 200:
if len(filename.split('.')) > 0 and filename.split('.')[-1] == 'png':
content_type = "Content-Type: image/png"
return Response(
response=data_data,
content_type=content_type,
status=status_code
)
else:
status_code, json_data, data_data, headers = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path2}/{content_server}?filename={filename}")
content_type = headers.get("Content-Type")
if len(filename.split('.')) > 0 and filename.split('.')[-1] == 'png':
content_type = "Content-Type: image/png"
return Response(
response=data_data,
content_type=content_type,
status=status_code
)
async def post_request(url, headers={}, json_inp=None, data=None, cookies=None):
async with aiohttp.ClientSession(cookies=cookies, skip_auto_headers={"User-Agent"}) as session:
async with session.post(url, headers=headers, json=json_inp, data=data) as r:
data = ""
json_data = ""
if hasattr(r, "data"):
data = await r.data
if r.content_type == 'application/json':
json_data = await r.json()
return r.status, json_data, data, r.headers
async def get_request(url, headers={}, cookies={}):
async with aiohttp.ClientSession(cookies=cookies, skip_auto_headers={"User-Agent"}) as session:
async with session.get(url, headers=headers) as r:
data = ""
json_data = ""
if r.content_type == 'application/json':
json_data = await r.json()
return r.status, json_data, data, r.headers
if hasattr(r, "data"):
data = r.data
elif hasattr(r, "content"):
data = await r.content.read()
return r.status, json_data, data, r.headers
@app.route('/file/get/<path>', methods=['GET'])
#@route_cors()
@user_cook
async def get_file(path): # put application's code here
async def check_access(username, filename):
status_code, _, _, _ = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/check_access?filename={filename}&username={username}")
if status_code == 200:
return True
else:
return False
filename = request.args.get("filename")
content_server = app.config.get("CONTENT_SERVER")
if not await check_access(session.get("username"), filename):
return abort(403)
status_code, json_data, data_data, header = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path}/{content_server}?filename={filename}")
content_type = header.get("Content-Type")
if len(filename.split('.')) > 0 and filename.split('.')[-1] == 'png':
content_type = "Content-Type: image/png"
return Response(
response=data_data,
content_type=content_type,
status=status_code
)
@app.route('/file/list')
#@route_cors()
async def file_list():
content_server = app.config.get("CONTENT_SERVER")
path = request.args.get('path')
status_code, json_data, _, headers = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/file_list/{path}/{content_server}")
return Response(
response=json.dumps(json_data),
content_type=headers.get('Content-Type'),
status=status_code)
@app.route('/file/upload', methods=['POST'])
#@route_cors()
@user_cook
async def upload_image():
if request.method == 'POST':
if 'file' not in await request.files:
return abort(403)
content_server = app.config.get("CONTENT_SERVER")
path = request.args.get("path")
username = session.get("username")
form_data = aiohttp.FormData()
file_bytes = (await request.files).get('file').stream.read()
filename = (await request.files).get("file").filename
form_data.add_field(name='file', value=file_bytes , filename=filename)
form_data.add_field(name="username", value=username)
form_data.add_field(name="filename", value=filename)
status_code, json_data, data_data, headers = await post_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path}/{content_server}",
data=form_data)
return Response(
response=json.dumps(json_data),
content_type=headers.get('Content-Type'),
status=status_code
)
@app.route('/api/auth/sign_up', methods=["POST"])
#@route_cors()
async def sign_up():
username = (await request.json).get("username")
password = (await request.json).get("password")
is_private = (await request.json).get("is_private")
if username and password:
url_id = generate('1234567890abcdef', 12)
user = User(username=username, blog_url=url_id)
user.set_password(password)
blog = Blog(url_id, is_private)
db.session.add(user)
db.session.add(blog)
db.session.commit()
session["username"] = user.username
return jsonify({"Status": "OK"})
else:
return abort(403)
@app.route('/api/auth/sign_in', methods=["POST"])
async def sign_in():
username = (await request.json).get("username")
password = (await request.json).get("password")
if username and password:
user = User.query.filter_by(username=username).first()
if user is None or not user.check_password(password):
return jsonify({"Result": "Bad creads"}), 400
else:
session["username"] = user.username
return jsonify({"Result": "OK"})
@app.route('/api/self_delete')
#@route_cors()
@user_cook
async def self_remove_acc():
username = session.get("username")
user = User.query.filter_by(username=username).first()
db.session.delete(user)
db.session.commit()
await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/delete/{username}/{app.config.get('CONTENT_SERVER')}")
return jsonify({"result": "OK"})
@app.route('/api/blog')
#@route_cors()
@user_cook
async def get_my_blog():
user = User.query.filter_by(username=session.get("username")).first()
blog = Blog.query.filter_by(url=user.blog_url).first()
return jsonify({"url": blog.url, "is_private": blog.is_private})
@app.route('/api/blogs')
#@route_cors()
async def get_blogs():
blogs = Blog.query.with_entities(Blog.url, Blog.is_private).all()
blogs_list = [{"url": i.url, "is_private": i.is_private} for i in blogs]
return jsonify(blogs_list)
@app.route('/api/blog/<url_id>')
#@route_cors()
@user_cook
async def blog_get_posts(url_id):
blog = Blog.query.filter_by(url=url_id).first()
if blog:
if blog.posts:
return jsonify({"posts": list(map(lambda x: {"id":x.id, "title":x.title}, blog.posts))})
else:
return jsonify({"error": "?"})
else:
return jsonify({"error": "?"})
@app.route('/api/blog/<url_id>/create_post', methods=["POST"])
#@route_cors()
@user_cook
async def blog_create_post(url_id):
blog = Blog.query.filter_by(url=url_id).first()
title = (await request.json).get("title")
body = (await request.json).get("body")
post = Post(title=title, body=body, blog_url=blog.url)
db.session.add(post)
db.session.commit()
this_post = Post.query.filter_by(title=title, body=body).all()[-1]
return jsonify({"Result": "OK", "post_id": this_post.id})
@app.route('/api/blog/<url_id>/post/<int:post_id>')
#@route_cors()
@user_cook
async def blog_read_post(url_id, post_id):
post = Post.query.filter_by(blog_url=url_id, id=post_id).first()
return jsonify({"title": post.title, "body": post.body})
if __name__ == '__main__':
app.run(port=13377, host="0.0.0.0", debug=False)