Merged release/1.0.0-beta into master
This commit is contained in:
commit
45f53223b0
13 changed files with 191 additions and 73 deletions
|
|
@ -92,6 +92,20 @@ def command_init_backend(**kwargs):
|
||||||
config.backend.paste.init()
|
config.backend.paste.init()
|
||||||
|
|
||||||
|
|
||||||
|
def command_sanitize_backend(**kwargs):
|
||||||
|
"""sanitize the backend
|
||||||
|
"""
|
||||||
|
|
||||||
|
from httpaste import load_config
|
||||||
|
|
||||||
|
config, _ = load_config(kwargs.get('config'))
|
||||||
|
|
||||||
|
config, _ = load_config(kwargs.get('config'))
|
||||||
|
|
||||||
|
config.backend.user.sanitize()
|
||||||
|
config.backend.paste.sanitize()
|
||||||
|
|
||||||
|
|
||||||
def parser():
|
def parser():
|
||||||
|
|
||||||
p = argparse.ArgumentParser(description='Process some integers.')
|
p = argparse.ArgumentParser(description='Process some integers.')
|
||||||
|
|
@ -121,6 +135,11 @@ def parser():
|
||||||
help=command_init_backend.__doc__)
|
help=command_init_backend.__doc__)
|
||||||
p_init_backend.add_argument('--config', '-c', required=True)
|
p_init_backend.add_argument('--config', '-c', required=True)
|
||||||
|
|
||||||
|
p_sanitize_backend = sp.add_parser(
|
||||||
|
'sanitize-backend',
|
||||||
|
help=command_sanitize_backend.__doc__)
|
||||||
|
p_sanitize_backend.add_argument('--config', '-c', required=True)
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -136,7 +155,8 @@ def main():
|
||||||
'cgi': command_cgi,
|
'cgi': command_cgi,
|
||||||
'fcgi': command_fcgi,
|
'fcgi': command_fcgi,
|
||||||
'default-config': command_default_config,
|
'default-config': command_default_config,
|
||||||
'init-backend': command_init_backend
|
'init-backend': command_init_backend,
|
||||||
|
'sanitize-backend': command_sanitize_backend
|
||||||
}[kwargs.pop('command')](**kwargs)
|
}[kwargs.pop('command')](**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class SQLite(Backend):
|
||||||
|
|
||||||
def __init__(self, parameters: SqliteParameters):
|
def __init__(self, parameters: SqliteParameters):
|
||||||
|
|
||||||
parameters['connection'] = get_sqlite_connection(parameters)
|
parameters = SqliteParameters(parameters.path, get_sqlite_connection(parameters))
|
||||||
|
|
||||||
self.user = SqliteUser(parameters, User)
|
self.user = SqliteUser(parameters, User)
|
||||||
self.paste = SqlitePaste(parameters, Paste)
|
self.paste = SqlitePaste(parameters, Paste)
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,13 @@ class User(object):
|
||||||
|
|
||||||
return user.init(self.path)
|
return user.init(self.path)
|
||||||
|
|
||||||
|
def sanitize(self):
|
||||||
|
|
||||||
|
if self.path.exists():
|
||||||
|
return user.sanitize(self.path, self.model_class, self.model_schema)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Paste(object):
|
class Paste(object):
|
||||||
"""Filesystem paste model backend
|
"""Filesystem paste model backend
|
||||||
|
|
@ -96,3 +103,10 @@ class Paste(object):
|
||||||
def init(self):
|
def init(self):
|
||||||
|
|
||||||
return paste.init(self.path)
|
return paste.init(self.path)
|
||||||
|
|
||||||
|
def sanitize(self):
|
||||||
|
|
||||||
|
if self.path.exists():
|
||||||
|
return paste.sanitize(self.path, self.model_class, self.model_schema)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
@ -5,6 +5,16 @@ acting as cells.
|
||||||
"""
|
"""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
|
||||||
|
COLUMNS = [
|
||||||
|
'data',
|
||||||
|
'data_hash',
|
||||||
|
'sub',
|
||||||
|
'expiration',
|
||||||
|
'encoding'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def load(
|
def load(
|
||||||
|
|
@ -22,13 +32,7 @@ def load(
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cells = {}
|
cells = {}
|
||||||
for column in [
|
for column in COLUMNS:
|
||||||
'data',
|
|
||||||
'data_hash',
|
|
||||||
'sub',
|
|
||||||
'timestamp',
|
|
||||||
'lifetime',
|
|
||||||
'encoding']:
|
|
||||||
|
|
||||||
cell = row.joinpath(column)
|
cell = row.joinpath(column)
|
||||||
|
|
||||||
|
|
@ -56,8 +60,7 @@ def load(
|
||||||
cells['sub'],
|
cells['sub'],
|
||||||
cells['data'],
|
cells['data'],
|
||||||
cells['data_hash'],
|
cells['data_hash'],
|
||||||
cells['timestamp'],
|
cells['expiration'],
|
||||||
cells['lifetime'],
|
|
||||||
cells['encoding'])
|
cells['encoding'])
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,13 +71,7 @@ def dump(model: object, path: Path, model_schema: type) -> None:
|
||||||
row = path.joinpath(model.pid.hex())
|
row = path.joinpath(model.pid.hex())
|
||||||
row.mkdir(parents=True, exist_ok=True)
|
row.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
for column in [
|
for column in COLUMNS:
|
||||||
'data',
|
|
||||||
'data_hash',
|
|
||||||
'sub',
|
|
||||||
'timestamp',
|
|
||||||
'lifetime',
|
|
||||||
'encoding']:
|
|
||||||
|
|
||||||
cell = row.joinpath(column)
|
cell = row.joinpath(column)
|
||||||
cell_schema = getattr(model_schema, column)
|
cell_schema = getattr(model_schema, column)
|
||||||
|
|
@ -102,6 +99,22 @@ def init(path: Path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize(path: Path, model_class: type, model_schema: type):
|
||||||
|
|
||||||
|
for row in path.iterdir():
|
||||||
|
|
||||||
|
expiration_cell = row.joinpath('expiration')
|
||||||
|
|
||||||
|
if not expiration_cell.exists():
|
||||||
|
continue
|
||||||
|
|
||||||
|
expiration = literal_eval(expiration_cell.read_text())
|
||||||
|
|
||||||
|
if expiration < int(time()) and expiration > 0:
|
||||||
|
|
||||||
|
delete(model_class(bytes.fromhex(row.name)), path)
|
||||||
|
|
||||||
|
|
||||||
def _rm_tree(pth: Path):
|
def _rm_tree(pth: Path):
|
||||||
for child in pth.iterdir():
|
for child in pth.iterdir():
|
||||||
if child.is_file():
|
if child.is_file():
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,12 @@ acting as cells.
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
|
|
||||||
|
COLUMNS = [
|
||||||
|
'sub',
|
||||||
|
'key_hash',
|
||||||
|
'index',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def load(
|
def load(
|
||||||
proto: object,
|
proto: object,
|
||||||
|
|
@ -22,15 +28,17 @@ def load(
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cells = {}
|
cells = {}
|
||||||
for column in ['key_hash', 'index']:
|
for column in COLUMNS[1:]:
|
||||||
|
|
||||||
cell = row.joinpath(column)
|
cell = row.joinpath(column)
|
||||||
|
|
||||||
if getattr(model_schema, column) == bytes:
|
if not cell.exists():
|
||||||
|
cells[column] = None
|
||||||
|
elif getattr(model_schema, column) == bytes:
|
||||||
cells[column] = cell.read_bytes()
|
cells[column] = cell.read_bytes()
|
||||||
|
elif getattr(model_schema, column) == str:
|
||||||
|
cells[column] = cell.read_text()
|
||||||
else:
|
else:
|
||||||
|
|
||||||
cells[column] = literal_eval(cell.read_text())
|
cells[column] = literal_eval(cell.read_text())
|
||||||
|
|
||||||
return model_class(
|
return model_class(
|
||||||
|
|
@ -46,7 +54,7 @@ def dump(model: object, path: Path, model_schema: object) -> None:
|
||||||
row = path.joinpath(model.sub.hex())
|
row = path.joinpath(model.sub.hex())
|
||||||
row.mkdir(parents=True, exist_ok=True)
|
row.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
for column in ['key_hash', 'index']:
|
for column in COLUMNS[1:]:
|
||||||
|
|
||||||
cell = row.joinpath(column)
|
cell = row.joinpath(column)
|
||||||
|
|
||||||
|
|
@ -74,6 +82,11 @@ def init(path: Path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize(path: Path, model_class: type, model_schema: type):
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _rm_tree(pth: Path):
|
def _rm_tree(pth: Path):
|
||||||
for child in pth.iterdir():
|
for child in pth.iterdir():
|
||||||
if child.is_file():
|
if child.is_file():
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ class User(object):
|
||||||
|
|
||||||
return user.init(self.connection)
|
return user.init(self.connection)
|
||||||
|
|
||||||
|
def sanitize(self):
|
||||||
|
|
||||||
|
return user.sanitize(self.connection, self.model_class)
|
||||||
|
|
||||||
|
|
||||||
class Paste(object):
|
class Paste(object):
|
||||||
"""SQLite paste model backend
|
"""SQLite paste model backend
|
||||||
|
|
@ -74,6 +78,10 @@ class Paste(object):
|
||||||
|
|
||||||
return paste.init(self.connection)
|
return paste.init(self.connection)
|
||||||
|
|
||||||
|
def sanitize(self):
|
||||||
|
|
||||||
|
return paste.sanitize(self.connection, self.model_class)
|
||||||
|
|
||||||
|
|
||||||
def get_connection(parameters: Parameters):
|
def get_connection(parameters: Parameters):
|
||||||
"""get an sqlite connection object
|
"""get an sqlite connection object
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
"""
|
"""
|
||||||
from os import path
|
from os import path
|
||||||
from sqlite3 import Connection
|
from sqlite3 import Connection
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
|
||||||
def load(proto: object, connection: Connection, model_class: type):
|
def load(proto: object, connection: Connection, model_class: type):
|
||||||
|
|
@ -11,7 +12,7 @@ def load(proto: object, connection: Connection, model_class: type):
|
||||||
cur = connection.cursor()
|
cur = connection.cursor()
|
||||||
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
'SELECT pid, data, data_hash, sub, timestamp, lifetime, encoding FROM pastes WHERE pid=?',
|
'SELECT pid, data, data_hash, sub, expiration, encoding FROM pastes WHERE pid=?',
|
||||||
(proto.pid,
|
(proto.pid,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
@ -24,8 +25,7 @@ def load(proto: object, connection: Connection, model_class: type):
|
||||||
result['sub'],
|
result['sub'],
|
||||||
result['data'],
|
result['data'],
|
||||||
result['data_hash'],
|
result['data_hash'],
|
||||||
result['timestamp'],
|
result['expiration'],
|
||||||
result['lifetime'],
|
|
||||||
result['encoding'])
|
result['encoding'])
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
@ -38,14 +38,13 @@ def dump(model: object, connection: Connection):
|
||||||
cur = connection.cursor()
|
cur = connection.cursor()
|
||||||
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
'''INSERT INTO pastes (pid, data, data_hash, sub, timestamp, lifetime, encoding)
|
'''INSERT INTO pastes (pid, data, data_hash, sub, expiration, encoding)
|
||||||
VALUES (?,?,?,?,?,?,?)''',
|
VALUES (?,?,?,?,?,?)''',
|
||||||
(model.pid,
|
(model.pid,
|
||||||
model.data,
|
model.data,
|
||||||
model.data_hash,
|
model.data_hash,
|
||||||
model.sub,
|
model.sub,
|
||||||
model.timestamp,
|
model.expiration,
|
||||||
model.lifetime,
|
|
||||||
model.encoding))
|
model.encoding))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
@ -69,3 +68,14 @@ def init(connection: Connection):
|
||||||
cur.execute(fh.read())
|
cur.execute(fh.read())
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize(connection: Connection, model_class: type) -> bool:
|
||||||
|
|
||||||
|
cur = connection.cursor()
|
||||||
|
|
||||||
|
cur.execute('''SELECT pid FROM pastes WHERE expiration < ? AND expiration > 0''', (int(time()),))
|
||||||
|
|
||||||
|
for row in cur.fetchall():
|
||||||
|
|
||||||
|
delete(model_class(row['pid']))
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
CREATE TABLE IF NOT EXISTS "pastes" (
|
CREATE TABLE IF NOT EXISTS "pastes" (
|
||||||
"id" BLOB NOT NULL UNIQUE,
|
"pid" BLOB NOT NULL UNIQUE,
|
||||||
"data" BLOB NOT NULL,
|
"data" BLOB NOT NULL,
|
||||||
"data_hash" BLOB NOT NULL,
|
"data_hash" BLOB NOT NULL,
|
||||||
"sub" BLOB UNIQUE,
|
"sub" BLOB UNIQUE,
|
||||||
"timestamp" INTEGER NOT NULL,
|
"expiration" INTEGER NOT NULL,
|
||||||
"lifetime" INTEGER NOT NULL,
|
"encoding" TEXT,
|
||||||
"encoding" TEXT
|
PRIMARY KEY("pid")
|
||||||
PRIMARY KEY("id")
|
|
||||||
);
|
);
|
||||||
|
|
@ -53,3 +53,8 @@ def init(connection: Connection):
|
||||||
cur.execute(fh.read())
|
cur.execute(fh.read())
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize(connection: Connection, model_class) -> bool:
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
@ -71,7 +71,7 @@ def get(**kwargs):
|
||||||
config.salt, config.hmac_iterations)
|
config.salt, config.hmac_iterations)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data, lifetime, encoding = call()
|
data, expiration, encoding = call()
|
||||||
except paste_model.LifetimeError as e:
|
except paste_model.LifetimeError as e:
|
||||||
if kwargs.get('user') is not None:
|
if kwargs.get('user') is not None:
|
||||||
paste_model.remove_safe(pid, sub, pkey, config.backend.paste,
|
paste_model.remove_safe(pid, sub, pkey, config.backend.paste,
|
||||||
|
|
@ -85,7 +85,7 @@ def get(**kwargs):
|
||||||
raise ForbiddenError(str(e))
|
raise ForbiddenError(str(e))
|
||||||
|
|
||||||
# burn after read
|
# burn after read
|
||||||
if lifetime < 0:
|
if expiration < 0:
|
||||||
if kwargs.get('user') is not None:
|
if kwargs.get('user') is not None:
|
||||||
paste_model.remove_safe(pid, sub, pkey, config.backend.paste,
|
paste_model.remove_safe(pid, sub, pkey, config.backend.paste,
|
||||||
config.salt, config.hmac_iterations)
|
config.salt, config.hmac_iterations)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"""Model
|
"""Model
|
||||||
"""
|
"""
|
||||||
from typing import NamedTuple, Optional, Dict, Union
|
from typing import NamedTuple, Optional, Dict, Union, Any, TypedDict
|
||||||
|
|
||||||
|
|
||||||
class PasteDataSchema:
|
class PasteDataSchema:
|
||||||
|
|
@ -12,6 +12,7 @@ class PasteDataSchema:
|
||||||
sub = bytes
|
sub = bytes
|
||||||
timestamp = int
|
timestamp = int
|
||||||
lifetime = int
|
lifetime = int
|
||||||
|
expiration = int
|
||||||
encoding = str
|
encoding = str
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -54,6 +55,14 @@ class PasteEncoding(PasteDataSchema.encoding):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class PasteExpiration(PasteDataSchema.expiration):
|
||||||
|
"""Paste Expiration
|
||||||
|
|
||||||
|
< 0: after first acccess
|
||||||
|
0: never
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class PasteLifetime(PasteDataSchema.lifetime):
|
class PasteLifetime(PasteDataSchema.lifetime):
|
||||||
"""Paste Lifetime
|
"""Paste Lifetime
|
||||||
"""
|
"""
|
||||||
|
|
@ -89,9 +98,11 @@ class Sub(UserDataSchema.sub):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Index(Dict[PasteId, PasteKey]):
|
class Index(TypedDict):
|
||||||
"""User Paste Index
|
"""User Paste Index
|
||||||
"""
|
"""
|
||||||
|
auth_expires: int
|
||||||
|
pastes: Dict[str, Dict[str, Any]]
|
||||||
|
|
||||||
|
|
||||||
class SerializedIndex(UserDataSchema.index):
|
class SerializedIndex(UserDataSchema.index):
|
||||||
|
|
@ -128,8 +139,6 @@ class Paste(NamedTuple):
|
||||||
#: paste data hash
|
#: paste data hash
|
||||||
data_hash: Optional[PasteHash] = None
|
data_hash: Optional[PasteHash] = None
|
||||||
#: paste timestamp
|
#: paste timestamp
|
||||||
timestamp: Optional[PasteTimestamp] = None
|
expiration: Optional[PasteExpiration] = None
|
||||||
#: paste lifetime
|
|
||||||
lifetime: Optional[PasteLifetime] = None
|
|
||||||
#: paste encoding
|
#: paste encoding
|
||||||
encoding: Optional[PasteEncoding] = None
|
encoding: Optional[PasteEncoding] = None
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from httpaste.helper.crypto import dhash, shash, encrypt, decrypt
|
||||||
from httpaste.helper.common import generate_random_string
|
from httpaste.helper.common import generate_random_string
|
||||||
from httpaste.model import (Paste, PasteId, Sub, MasterKey, PasteKey, Salt,
|
from httpaste.model import (Paste, PasteId, Sub, MasterKey, PasteKey, Salt,
|
||||||
PasteData, PasteHash, PasteTimestamp, PasteSub,
|
PasteData, PasteHash, PasteTimestamp, PasteSub,
|
||||||
PasteLifetime, PasteEncoding)
|
PasteLifetime, PasteEncoding, PasteExpiration)
|
||||||
|
|
||||||
|
|
||||||
class NotFoundError(Exception):
|
class NotFoundError(Exception):
|
||||||
|
|
@ -87,8 +87,7 @@ def load(proto: Paste, backend: object) -> Optional[Paste]:
|
||||||
|
|
||||||
raise SubError('Paste not owned by user')
|
raise SubError('Paste not owned by user')
|
||||||
|
|
||||||
if model.lifetime >= 0 and model.timestamp + \
|
if model.expiration > 0 and model.expiration < int(time.time()):
|
||||||
(60 * model.lifetime) < int(time.time()):
|
|
||||||
|
|
||||||
raise LifetimeError('Paste expired')
|
raise LifetimeError('Paste expired')
|
||||||
|
|
||||||
|
|
@ -121,8 +120,7 @@ def load_safe(
|
||||||
proto.sub,
|
proto.sub,
|
||||||
data,
|
data,
|
||||||
model.data_hash,
|
model.data_hash,
|
||||||
model.timestamp,
|
model.expiration,
|
||||||
model.lifetime,
|
|
||||||
model.encoding)
|
model.encoding)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -195,6 +193,11 @@ def create(
|
||||||
sub = None
|
sub = None
|
||||||
timestamp = PasteTimestamp(int(time.time()))
|
timestamp = PasteTimestamp(int(time.time()))
|
||||||
|
|
||||||
|
if lifetime < 0:
|
||||||
|
expiration = -1
|
||||||
|
else:
|
||||||
|
expiration = PasteExpiration(timestamp + (lifetime * 60))
|
||||||
|
|
||||||
safe_data = PasteData(encrypt(data, pid, salt, hmac_iter))
|
safe_data = PasteData(encrypt(data, pid, salt, hmac_iter))
|
||||||
|
|
||||||
model = Paste(
|
model = Paste(
|
||||||
|
|
@ -202,8 +205,7 @@ def create(
|
||||||
sub,
|
sub,
|
||||||
safe_data,
|
safe_data,
|
||||||
data_hash,
|
data_hash,
|
||||||
timestamp,
|
expiration,
|
||||||
lifetime,
|
|
||||||
encoding)
|
encoding)
|
||||||
|
|
||||||
dump(model, backend)
|
dump(model, backend)
|
||||||
|
|
@ -234,6 +236,11 @@ def create_safe(data: PasteData,
|
||||||
safe_sub = PasteSub(shash(sub, data_hash, pid))
|
safe_sub = PasteSub(shash(sub, data_hash, pid))
|
||||||
timestamp = PasteTimestamp(int(time.time()))
|
timestamp = PasteTimestamp(int(time.time()))
|
||||||
|
|
||||||
|
if lifetime < 0:
|
||||||
|
expiration = -1
|
||||||
|
else:
|
||||||
|
expiration = PasteExpiration(timestamp + (lifetime * 60))
|
||||||
|
|
||||||
safe_data = PasteData(encrypt(data, pkey, salt, hmac_iter))
|
safe_data = PasteData(encrypt(data, pkey, salt, hmac_iter))
|
||||||
|
|
||||||
dump(Paste(
|
dump(Paste(
|
||||||
|
|
@ -241,8 +248,7 @@ def create_safe(data: PasteData,
|
||||||
safe_sub,
|
safe_sub,
|
||||||
safe_data,
|
safe_data,
|
||||||
data_hash,
|
data_hash,
|
||||||
timestamp,
|
expiration,
|
||||||
lifetime,
|
|
||||||
encoding
|
encoding
|
||||||
), backend)
|
), backend)
|
||||||
|
|
||||||
|
|
@ -279,7 +285,7 @@ def get(pid: PasteId, backend: object, salt: Salt = Config.salt, hmac_iter: int
|
||||||
|
|
||||||
data = decrypt(model.data, pid, salt, hmac_iter)
|
data = decrypt(model.data, pid, salt, hmac_iter)
|
||||||
|
|
||||||
return PasteData(data), model.lifetime, model.encoding
|
return PasteData(data), model.expiration, model.encoding
|
||||||
|
|
||||||
|
|
||||||
def get_safe(
|
def get_safe(
|
||||||
|
|
@ -294,4 +300,4 @@ def get_safe(
|
||||||
|
|
||||||
model = load_safe(Paste(pid, sub), pkey, backend, salt, hmac_iter)
|
model = load_safe(Paste(pid, sub), pkey, backend, salt, hmac_iter)
|
||||||
|
|
||||||
return PasteData(model.data), model.lifetime, model.encoding
|
return PasteData(model.data), model.expiration, model.encoding
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
"""user model interface
|
"""user model interface
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
from time import time
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from httpaste import Config
|
from httpaste import Config
|
||||||
|
|
@ -34,7 +35,7 @@ class IndexError(Exception):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def load(
|
def _load(
|
||||||
proto: User,
|
proto: User,
|
||||||
master_key: str,
|
master_key: str,
|
||||||
backend: object,
|
backend: object,
|
||||||
|
|
@ -54,16 +55,19 @@ def load(
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
serialized_data = decrypt(model.index, master_key, salt, hmac_iter)
|
||||||
|
except DecryptionError as e:
|
||||||
|
raise IndexError('unable to decrypt user index') from e
|
||||||
|
else:
|
||||||
|
data = json.loads(serialized_data)
|
||||||
|
|
||||||
return User(
|
return User(
|
||||||
*model[:-1],
|
*model[:-1],
|
||||||
Index(**json.loads(decrypt(model.index, master_key, salt, hmac_iter)))
|
Index(**data)
|
||||||
)
|
)
|
||||||
except DecryptionError as e:
|
|
||||||
|
|
||||||
raise IndexError('unable to decrypt user index') from e
|
|
||||||
|
|
||||||
|
|
||||||
def dump(
|
def _dump(
|
||||||
model: User,
|
model: User,
|
||||||
key: MasterKey,
|
key: MasterKey,
|
||||||
backend: object,
|
backend: object,
|
||||||
|
|
@ -77,7 +81,7 @@ def dump(
|
||||||
:param salt: randomization salt
|
:param salt: randomization salt
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(model.index, Index):
|
if model.index is not None and not isinstance(model.index, dict):
|
||||||
|
|
||||||
raise BaseException('index serialization pre-processing not allowed.')
|
raise BaseException('index serialization pre-processing not allowed.')
|
||||||
|
|
||||||
|
|
@ -102,11 +106,13 @@ def load_paste_key(
|
||||||
:param salt: randomization salt
|
:param salt: randomization salt
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for k, v in load(User(sub), key, backend, salt, hmac_iter).index.items():
|
model = _load(User(sub), key, backend, salt, hmac_iter)
|
||||||
|
|
||||||
|
for k, v in model.index.get('pastes').items():
|
||||||
|
|
||||||
if bytes.fromhex(k) == pid:
|
if bytes.fromhex(k) == pid:
|
||||||
|
|
||||||
return PasteKey(bytes.fromhex(v))
|
return PasteKey(bytes.fromhex(v.get('key')))
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -128,12 +134,13 @@ def dump_paste_key(
|
||||||
:param backend: user model backend
|
:param backend: user model backend
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = load(User(sub), key, backend, salt, hmac_iter)
|
model = _load(User(sub), key, backend, salt, hmac_iter)
|
||||||
|
|
||||||
dump(User(*model[:-1], Index({
|
model.index.setdefault('pastes', {})[pid.hex()] = {
|
||||||
**model.index,
|
'key': pkey.hex()
|
||||||
**{pid.hex(): pkey.hex()}
|
}
|
||||||
})), key, backend, salt, hmac_iter)
|
|
||||||
|
_dump(model, key, backend, salt, hmac_iter)
|
||||||
|
|
||||||
|
|
||||||
def authenticate(
|
def authenticate(
|
||||||
|
|
@ -154,22 +161,36 @@ def authenticate(
|
||||||
|
|
||||||
proto = User(sub)
|
proto = User(sub)
|
||||||
|
|
||||||
|
bogus_decline_msg = 'unable to authenticate'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
model = load(proto, key, backend, salt, hmac_iter)
|
model = _load(proto, key, backend, salt, hmac_iter)
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
raise AuthenticationError('you dun goofed')
|
raise AuthenticationError(bogus_decline_msg) from e
|
||||||
|
|
||||||
if not model:
|
if not model:
|
||||||
|
|
||||||
model = User(sub, key_hash, Index({}))
|
data = {
|
||||||
dump(model, key, backend, salt, hmac_iter)
|
'auth_expires': int(time()) + (1 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
model = User(sub, key_hash, Index(data))
|
||||||
|
_dump(model, key, backend, salt, hmac_iter)
|
||||||
else:
|
else:
|
||||||
|
|
||||||
if model.key_hash != key_hash:
|
if model.key_hash != key_hash:
|
||||||
|
|
||||||
raise AuthenticationError('you dun goofed')
|
raise AuthenticationError(bogus_decline_msg)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'sub': sub,
|
'sub': sub,
|
||||||
'master_key': key
|
'master_key': key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
AuthenticationError,
|
||||||
|
load_paste_key,
|
||||||
|
dump_paste_key,
|
||||||
|
authenticate
|
||||||
|
]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue