httpaste/src/httpaste/__init__.py
2022-04-15 03:54:27 +02:00

248 lines
7 KiB
Python
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""PSEUDO-MAN-PAGE
NAME
httpaste - versatile HTTP pastebin
SYNOPSIS
HTTP [POST|PUT|DELETE|GET] {url}paste/[public|private]
DESCRIPTION
This program offers an HTTP interface for storing public and private data
(a.k.a. pastes). Public data can be accessed through an URL, where as
private pastes additionally require HTTP basic authentication. Creation of
authentication credentials happens on the fly, there is no sign-up process.
Public pastes can only be accessed by knowing their paste ids, they are not
listed on any index, since it isn't technically possible (by design).
All pastes are symetrically encrypted with an HMAC derived key using
{hmac_iterations} iterations and SHA-256 hashing, a server-side salt and a
randomly generated password. Public paste's passwords are derived from
their ids. Private paste's passwords are randomly generated and stored
inside a symetrically encrypted personal database, with the encryption key
also being derived through the same HMAC mechanism, where the HTTP basic
authentication credentials act as the master password.
Paste ids, usernames, and any other identifiable attributes are only stored
inside databases as keyed and salted BLAKE2 hashes.
Default Paste Encoding: {paste_default_encoding}
Default Paste Lifetime: {paste_lifetime} minutes
Minimum Paste Lifetime: 1 minute
Maximum Paste Lifetime: {paste_max_lifetime} hours
EXAMPLES
POST Public Paste
$ echo '#My public paste' | curl {url}paste/public \\
-F 'data=<-'
{url}/paste/private/I0ah7fyA
GET Public Paste
$ curl {url}/paste/public/I0ah7fyA
#My public paste
POST Private Paste
$ echo '#My private paste' | curl {url}paste/private \\
-F 'data=<-' \\
-u myusername:mypassword
{url}paste/private/4FtNL75g
GET Private Paste
$ curl {url}paste/private -u myusername:mypassword
#My private paste
POST Paste (with non-default expiration)
$ echo '#My paste expires in 20 minutes' | curl \\
{url}paste/public?lifetime=20 \\
-F 'data=<-' \\
{url}paste/public/xMxEmNi8
POST Paste (with expiration after first read)
$ echo '#My paste expires after first read' | curl \\
{url}paste/public?lifetime=-1 \\
-F 'data=<-' \\
{url}paste/public/4FtNL75g
POST Paste with binary data
$ cat my.pdf | base64 | curl \\
"{url}paste/public?encoding=base64" \\
-F "data=<-"
{url}paste/public/zYWpEzXU
GET Paste (with shell syntax highlighting)
$ curl "{url}paste/public/I0ah7fyA?syntax=json"
#My public paste
GET Paste (with line numbers)
$ curl "{url}paste/public/I0ah7fyA?syntax=shell&linenos=true"
0001: #My public paste
0002:
GET Paste (with formatted output)
$ curl "{url}paste/public/I0ah7fyA?syntax=shell&format=html"
--e.g. HTML OUTPUT WITH INLINE CSS--
GET Paste (and set HTTP response content type)
$ curl "{url}paste/public/zYWpEzXU?mime=application/pdf"
--e.g. BINARY--
SEE ALSO
Documentation <https://victorykit.bitbucket.io/httpaste>
Sources <https://bitbucket.org/victorykit/httpaste>
Host (HTTP) <http://httpaste.it>
(Onion) <http://paste77ubkwxy4fqezffsmthxdh3xerwi72tlsw2mch7ecjhw2xn7iyd.onion>
NOTES
THIS PROGRAM IS FREE SOFTWARE.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
"""
from typing import NamedTuple
from configparser import ConfigParser
from importlib.resources import path as resource_path
from pathlib import Path
from connexion import FlaskApp
from connexion.resolver import RestyResolver
from httpaste.server import get_server_config
from httpaste.server import Config as ServerConfig
from httpaste.context import get_context_config
from httpaste.context import Config as ContextConfig
from httpaste.model import get_model_config
from httpaste.model import Config as ModelConfig
from httpaste.backend import get_backend_config
from httpaste.backend import Config as BackendConfig
from httpaste.helper.config import get_configparser, CONFIGPATH_ENVIRON
from httpaste.helper.http import (
BadRequestError,
ForbiddenError,
GoneError,
NotFoundError,
UnauthorizedError)
class Config(NamedTuple):
"""
"""
context: ContextConfig
server: ServerConfig
model: ModelConfig
backend: BackendConfig
def get_config(configIni: ConfigParser, path: Path):
"""
"""
from httpaste.model import Config as ModelConfig
context_config = get_context_config(configIni)
server_config = get_server_config(configIni)
model_config = get_model_config(configIni, path)
backend_config = get_backend_config(configIni, path)
return Config(
context=context_config,
server=server_config,
model=model_config,
backend=backend_config
)
def load_config(path: str = None, var_name: str = CONFIGPATH_ENVIRON):
"""
"""
configIni, path = get_configparser(path, var_name)
return get_config(configIni, Path(path).resolve().parent)
def get_flask_app(config: Config) -> FlaskApp:
"""get a flask app object
"""
print(config.server.swagger_ui)
options = {"swagger_ui": config.server.swagger_ui}
#context manager returns a pathlib.Path object
with resource_path('httpaste.schema', 'httpaste.openapi.json') as path:
application = FlaskApp(__name__, specification_dir=path.parent)
application.add_api(
path.name,
options=options,
resolver=RestyResolver('httpaste.controller')
)
for err_cls in [
BadRequestError,
ForbiddenError,
GoneError,
NotFoundError,
UnauthorizedError
]:
application.add_error_handler(
err_cls, getattr(err_cls, 'render')
)
with application.app.app_context():
application.app.httpaste = config
#add header for browsers to present a sign-in prompt
@application.app.after_request
def rewrite_forbidden_request(response):
if response.status_code in [401]:
response.headers['WWW-Authenticate'] = 'Basic realm="private"'
return response
return application
__all__ = [
Config,
load_config,
get_flask_app
]