15 Commits

Author SHA1 Message Date
ba68674e4e Add GitHub-specific README.md which links Gitea 2020-09-07 20:31:24 -05:00
99ff2c68a3 Change references of gitea.bbaovanc.com to git.bbaovanc.com 2020-09-07 02:07:36 -05:00
dd069bf395 Add newline after block comments at beginning 2020-09-06 15:24:20 -05:00
f21adfa04e Added FAQ and links to sections in README.md 2020-09-06 12:43:44 -05:00
3d5c55498f Clean up and add LICENSE section to README.md 2020-09-06 11:26:13 -05:00
5e2be10434 Change references of GitHub to gitea.bbaovanc.com 2020-09-06 11:09:06 -05:00
7c1f449bce Add "verify" field to request to not save image
This makes it easy for the user to debug authentication.
2020-09-05 18:55:56 -05:00
0dbcc0e380 Change file extension check to be case-insensitive 2020-09-05 16:21:50 -05:00
b8b5a2518c Change abort() calls to JSON responses
This makes the responses more consistent. Now, all responses are JSON.
2020-09-05 15:43:36 -05:00
805e545b39 Deduplicate code in keyctl.py and add comments 2020-09-04 19:44:46 -05:00
9a117817f7 Changed random to secrets for cryptographic security 2020-09-04 14:32:53 -07:00
c9cd6469a9 Change a couple print statements to logging.info
Quiet mode is now a little more useful!
2020-09-04 15:37:34 -05:00
6f64890e34 Remove unused imports from imgupload.py 2020-09-04 15:24:49 -05:00
91db522363 Add keyctl.py for easy management of uploadkeys 2020-09-04 15:19:40 -05:00
9d9b93a9ee Add proper shebangs and block comments 2020-09-04 10:47:20 -05:00
7 changed files with 268 additions and 17 deletions

7
.github/README.md vendored Normal file
View File

@ -0,0 +1,7 @@
# imgupload
## Moving from GitHub to Gitea
**TL;DR: Please go to my Gitea instance instead of GitHub for anything related to imgupload. [https://git.bbaovanc.com/bbaovanc/imgupload](https://git.bbaovanc.com/bbaovanc/imgupload)**
This repository might not exist on GitHub in the future! Releases will not be released here in the future. Instead, they will be released on the repository on my Gitea instance, which you can find [here](https://git.bbaovanc.com/bbaovanc/imgupload). Issues and pull requests should also be created on Gitea. For now, commits will still be pushed to this repository, but that may change in the future.

View File

@ -1,26 +1,63 @@
# imgupload
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/BBaoVanC/imgupload/master?color=purple) ![GitHub repo size](https://img.shields.io/github/repo-size/bbaovanc/imgupload?color=purple) ![GitHub All Releases](https://img.shields.io/github/downloads/bbaovanc/imgupload/total?color=purple) ![GitHub issues](https://img.shields.io/github/issues/bbaovanc/imgupload?color=purple) ![GitHub closed issues](https://img.shields.io/github/issues-closed/bbaovanc/imgupload?color=purple) ![GitHub](https://img.shields.io/github/license/bbaovanc/imgupload?color=purple)
### What is imgupload?
<!---![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/BBaoVanC/imgupload/master?color=purple)
![GitHub repo size](https://img.shields.io/github/repo-size/bbaovanc/imgupload?color=purple)
![GitHub All Releases](https://img.shields.io/github/downloads/bbaovanc/imgupload/total?color=purple)
![GitHub issues](https://img.shields.io/github/issues/bbaovanc/imgupload?color=purple)
![GitHub closed issues](https://img.shields.io/github/issues-closed/bbaovanc/imgupload?color=purple)
![GitHub](https://img.shields.io/github/license/bbaovanc/imgupload?color=purple)-->
## What is imgupload?
imgupload is a Flask + uWSGI application to serve as an all-purpose image/file uploader over POST requests.
### Installation
1. Clone the repository: `git clone https://github.com/BBaoVanC/imgupload.git`
---
## FAQ
**Where can I send bug reports and feature requests?**
You can create an issue [here](https://git.bbaovanc.com/bbaovanc/imgupload/issues).
**How do I use this program?**
See [Installation](#installation)
**I want to make a pull request. Where should I do that?**
First, fork [this repository](https://git.bbaovanc.com/bbaovanc/imgupload). If you don't have an account on my Gitea site yet, you can either create one, or sign in using your GitHub account. Commit your changes to your fork, and then create a pull request.
---
## Installation
1. Clone the repository: `git clone https://git.bbaovanc.com/bbaovanc/imgupload.git`
2. Enter the imgupload directory: `cd imgupload`
3. Create a virtualenv: `python3 -m venv env`
4. Enter the virtualenv: `source env/bin/activate`
5. Install dependencies: `python3 -m pip install -r requirements.txt`
6. Run the Flask app
6. [Run the Flask app](#running-the-flask-app)
---
## Running the Flask app
### Using uWSGI
[https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html](https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html)
Instructions specific to imgupload are coming soon
### Using Flask development server
```shell
$ source env/bin/activate # if you haven't already entered the virtualenv
$ export FLASK_APP=imgupload.py
$ flask run
```
---
## License
_imgupload_ is licensed under the GPLv3 license. For more information, please refer to [`LICENSE`](https://git.bbaovanc.com/bbaovanc/imgupload/src/branch/master/LICENSE)

View File

@ -1,3 +1,10 @@
#!/usr/bin/env python3
"""
configtest.py
Tests the validity of your configuration in settings.py.
"""
import os
import settings as settings

View File

@ -1,3 +1,10 @@
#!/usr/bin/env python3
"""
functions.py
Functions used by imgupload which can be easily customized.
"""
import string
import random

View File

@ -1,8 +1,13 @@
from flask import Flask, request, jsonify, abort, Response
from cryptography.fernet import Fernet
#!/usr/bin/env python3
"""
imgupload.py
Flask application for processing images uploaded through POST requests.
"""
from flask import Flask, request, jsonify, Response
from flask_api import status
from pathlib import Path
import random
import os
import datetime
@ -13,7 +18,7 @@ app = Flask(__name__) # app is the app
def allowed_extension(testext):
if testext in settings.ALLOWED_EXTENSIONS:
if testext.lower() in settings.ALLOWED_EXTENSIONS:
return True
else:
return False
@ -45,6 +50,11 @@ def upload():
if request.form["uploadKey"] in validkeys: # check if uploadKey is valid
print("Key is valid!")
if "verify" in request.form.keys():
if request.form["verify"] == "true":
print("Request is asking if key is valid (it is)")
return jsonify({'status': 'key_valid'})
if "imageUpload" in request.files: # check if image to upload was provided
f = request.files["imageUpload"] # f is the image to upload
else:
@ -77,21 +87,16 @@ def upload():
else: # if the extension was invalid
print("Uploaded extension is invalid!")
abort(415)
return jsonify({'status': 'error', 'error': 'INVALID_EXTENSION'}), status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
else: # if the key was not valid
print("Key is invalid!")
print("Request key: {0}".format(request.form["uploadKey"]))
abort(401)
return jsonify({'status': 'error', 'error': 'UNAUTHORIZED'}), status.HTTP_401_UNAUTHORIZED
else: # if uploadKey was not found in request body
print("No uploadKey found in request!")
abort(401)
else: # if the request method wasn't post
print("Request method was not POST!")
abort(405)
return jsonify({'status': 'error', 'error': 'UNAUTHORIZED'}), status.HTTP_401_UNAUTHORIZED
if __name__ == "__main__":
print("Run with `flask` or a WSGI server!")

181
keyctl.py Normal file
View File

@ -0,0 +1,181 @@
#!/usr/bin/env python3
"""
keyctl.py
Command-line utility for easy management of the uploadkeys file.
"""
from pathlib import Path
import argparse
import logging
import secrets
import string
def read_keyfile():
with open("uploadkeys", "r") as keyfile: # open uploadkeys
keys = keyfile.readlines() # read all the keys
logging.debug("Read uploadkeys")
keys = [x.strip("\n") for x in keys] # strip newlines from keys
logging.debug("Stripped newlines from keys")
return keys
def genkey(length):
key = ''.join(secrets.choice(string.ascii_letters + string.digits) for x in range(length))
return key
def savekey(key):
if not Path("uploadkeys").is_file(): # if uploadkeys doesn't exist, log an info message
logging.info("uploadkeys file doesn't exist, it will be created.")
with open("uploadkeys", "a+") as keyfile:
keyfile.write(str(key) + "\n") # add the key
logging.debug("Saved a key to uploadkeys: {0}".format(key))
def rmkey(delkey):
removedkey = False
allkeys = read_keyfile()
if delkey in allkeys: # if the key to remove exists
allkeys.remove(delkey) # remove the first instance of the key
removedkey = True
logging.debug("Removed one instance of the key")
with open("uploadkeys", "w") as keyfile:
for k in allkeys:
keyfile.write(k + "\n") # write the remaining keys
if removedkey:
return True
else:
return False
def find_duplicates():
allkeys = read_keyfile()
seen = set()
ukeys = []
dupkeys = []
for x in allkeys:
if x not in seen:
ukeys.append(x)
seen.add(x)
else:
dupkeys.append(x)
return dupkeys
def get_keys():
validkeys = read_keyfile()
while "" in validkeys:
validkeys.remove("")
logging.debug("Removed blank keys")
return validkeys
def cmd_list(args):
validkeys = get_keys()
print("List of upload keys:")
for i in range(len(validkeys)):
showkey = validkeys[i][:6]
if len(validkeys[i]) > 6:
showkey += "..." # add ellipses since the key was shortened in list
print(" [{0}] {1}".format(i+1, showkey))
def cmd_generate(args):
k = genkey(args.length)
logging.debug("Generated a new key: {0}".format(k))
savekey(k)
print("Your new key is: {0}".format(k))
def cmd_add(args):
print("Please type/paste the key you would like to add.")
akr = input("> ")
ak = akr.strip()
print()
logging.debug("Ran strip() on key")
print(ak)
if input("Is the above key correct? [y/N] ").lower() == "y":
logging.debug("Interpreted as yes")
ask_for_key = False
savekey(ak)
logging.info("Added.")
else:
logging.debug("Interpreted as no")
print("No key has been saved.")
def cmd_remove(args):
if rmkey(args.key):
logging.debug("Successfully removed the requested key")
else:
logging.info("No key was removed.")
def cmd_dedupe(args):
dupes = find_duplicates()
if len(dupes) > 0:
for d in dupes:
r = rmkey(d)
logging.debug(r)
logging.info("Removed duplicate key: {0}".format(d))
else:
logging.info("[" + u"\u2713" + "] No duplicate keys found!")
def cmd_show(args):
for k in get_keys():
if k[:6] == args.prefix:
print("Key: {0}".format(k))
break
parser = argparse.ArgumentParser() # create instance of argument parser class
parlog = parser.add_mutually_exclusive_group()
parlog.add_argument("-v", "--verbose", help="show debugging messages", action="store_true")
parlog.add_argument("-q", "--quiet", help="show only warning messages and up", action="store_true")
subparsers = parser.add_subparsers(help="sub-commands")
parser_list = subparsers.add_parser("list", help="list the beginning of each key")
parser_list.set_defaults(func=cmd_list)
parser_gen = subparsers.add_parser("generate", help="generate a key and save it to uploadkeys")
parser_gen.add_argument("length", help="length of key to generate", default=64, type=int, nargs="?")
parser_gen.set_defaults(func=cmd_generate)
parser_add = subparsers.add_parser("add", help="prompts for a key to add to uploadkeys")
parser_add.set_defaults(func=cmd_add)
parser_remove = subparsers.add_parser("remove", help="remove (one instance of) a key from uploadkeys")
parser_remove.add_argument("key", help="key to remove")
parser_remove.set_defaults(func=cmd_remove)
parser_dedupe = subparsers.add_parser("dedupe", help="remove duplicate keys")
parser_dedupe.set_defaults(func=cmd_dedupe)
parser_show = subparsers.add_parser("show", help="show the full key based on the first 6 characters")
parser_show.add_argument("prefix", help="first 6 characters of key (shown by `python3 keyctl.py list`)")
parser_show.set_defaults(func=cmd_show)
args = parser.parse_args() # parse the arguments
if args.verbose:
loglevel = logging.DEBUG
elif args.quiet:
loglevel = logging.WARNING
else:
loglevel = logging.INFO
logging.basicConfig(level=loglevel, format="%(levelname)s: %(message)s")
try:
args.func(args)
except AttributeError:
logging.error("AttributeError")
parser.print_help()

View File

@ -1,3 +1,10 @@
#!/usr/bin/env python3
"""
settings.py
User-defined settings used by imgupload.py.
"""
UPLOAD_FOLDER = "/path/to/images"
ALLOWED_EXTENSIONS = [".png", ".jpg", ".jpeg", ".svg", ".bmp", ".gif", ".ico", ".webp"]
ROOTURL = "https://example.com/"