Compare commits
10 commits
api-rate-l
...
main
Author | SHA1 | Date | |
---|---|---|---|
65137b99ac | |||
7f36eafc11 | |||
fc77407379 | |||
0644d260de | |||
8641f7c2ba | |||
6804360352 | |||
f8a67e5fbd | |||
3fb4d18db8 | |||
e6bbd01ea9 | |||
6463f6f9a2 |
12 changed files with 103 additions and 149 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
The 3-Clause BSD License
|
The 3-Clause BSD License
|
||||||
|
|
||||||
Copyright 2022 Ivan Golikov
|
Copyright 2025 Ivan Golikov
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Pssecret server
|
# Pssecret server
|
||||||
|
|
||||||
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||||
|
[![PyPI - Downloads](https://img.shields.io/pypi/dm/pssecret-server?label=PyPI%20downloads)](https://pypi.org/project/pssecret-server/)
|
||||||
|
|
||||||
Pssecret is self-hosted service to share secrets (like passwords) with somebody
|
Pssecret is self-hosted service to share secrets (like passwords) with somebody
|
||||||
over the network, but don't want them to appear in chats, unencrypted e-mails, etc.
|
over the network, but don't want them to appear in chats, unencrypted e-mails, etc.
|
||||||
|
@ -50,6 +51,7 @@ Available configuration options:
|
||||||
--uds TEXT Bind to a UNIX domain socket.
|
--uds TEXT Bind to a UNIX domain socket.
|
||||||
--workers INTEGER Number of worker processes. Defaults to the
|
--workers INTEGER Number of worker processes. Defaults to the
|
||||||
$WEB_CONCURRENCY environment variable if available, or 1.
|
$WEB_CONCURRENCY environment variable if available, or 1.
|
||||||
|
--version Show the version and exit.
|
||||||
--help Show this message and exit.
|
--help Show this message and exit.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
142
poetry.lock
generated
142
poetry.lock
generated
|
@ -1,4 +1,4 @@
|
||||||
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "annotated-types"
|
name = "annotated-types"
|
||||||
|
@ -186,6 +186,7 @@ files = [
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
|
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
|
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
|
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
|
||||||
|
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
|
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
|
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
|
{file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
|
||||||
|
@ -196,6 +197,7 @@ files = [
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
|
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
|
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
|
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
|
||||||
|
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
|
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
|
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
|
{file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
|
||||||
|
@ -233,23 +235,6 @@ files = [
|
||||||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "deprecated"
|
|
||||||
version = "1.2.15"
|
|
||||||
description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
|
|
||||||
optional = false
|
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
|
||||||
files = [
|
|
||||||
{file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"},
|
|
||||||
{file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
wrapt = ">=1.10,<2"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dnspython"
|
name = "dnspython"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
|
@ -686,34 +671,6 @@ MarkupSafe = ">=2.0"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
i18n = ["Babel (>=2.7)"]
|
i18n = ["Babel (>=2.7)"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "limits"
|
|
||||||
version = "3.14.1"
|
|
||||||
description = "Rate limiting utilities"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.9"
|
|
||||||
files = [
|
|
||||||
{file = "limits-3.14.1-py3-none-any.whl", hash = "sha256:051aca02da56e6932599a25cb8e70543959294f5d587d57bcd7e38df234e697b"},
|
|
||||||
{file = "limits-3.14.1.tar.gz", hash = "sha256:cad16a9b3cf3924e27da48e78bdab33ef312ecb7194fdb50e509cc8111c8d0bb"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
deprecated = ">=1.2"
|
|
||||||
packaging = ">=21,<25"
|
|
||||||
typing-extensions = "*"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
all = ["aetcd", "coredis (>=3.4.0,<5)", "emcache (>=0.6.1)", "emcache (>=1)", "etcd3", "motor (>=3,<4)", "pymemcache (>3,<5.0.0)", "pymongo (>4.1,<5)", "redis (>3,!=4.5.2,!=4.5.3,<6.0.0)", "redis (>=4.2.0,!=4.5.2,!=4.5.3)"]
|
|
||||||
async-etcd = ["aetcd"]
|
|
||||||
async-memcached = ["emcache (>=0.6.1)", "emcache (>=1)"]
|
|
||||||
async-mongodb = ["motor (>=3,<4)"]
|
|
||||||
async-redis = ["coredis (>=3.4.0,<5)"]
|
|
||||||
etcd = ["etcd3"]
|
|
||||||
memcached = ["pymemcache (>3,<5.0.0)"]
|
|
||||||
mongodb = ["pymongo (>4.1,<5)"]
|
|
||||||
redis = ["redis (>3,!=4.5.2,!=4.5.3,<6.0.0)"]
|
|
||||||
rediscluster = ["redis (>=4.2.0,!=4.5.2,!=4.5.3)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markdown-it-py"
|
name = "markdown-it-py"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
|
@ -1341,23 +1298,6 @@ files = [
|
||||||
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
|
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "slowapi"
|
|
||||||
version = "0.1.9"
|
|
||||||
description = "A rate limiting extension for Starlette and Fastapi"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7,<4.0"
|
|
||||||
files = [
|
|
||||||
{file = "slowapi-0.1.9-py3-none-any.whl", hash = "sha256:cfad116cfb84ad9d763ee155c1e5c5cbf00b0d47399a769b227865f5df576e36"},
|
|
||||||
{file = "slowapi-0.1.9.tar.gz", hash = "sha256:639192d0f1ca01b1c6d95bf6c71d794c3a9ee189855337b4821f7f457dddad77"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
limits = ">=2.3"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
redis = ["redis (>=3.4.1,<4.0.0)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
|
@ -1696,84 +1636,10 @@ files = [
|
||||||
{file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"},
|
{file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wrapt"
|
|
||||||
version = "1.17.0"
|
|
||||||
description = "Module for decorators, wrappers and monkey patching."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"},
|
|
||||||
{file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"},
|
|
||||||
{file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"},
|
|
||||||
{file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"},
|
|
||||||
{file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"},
|
|
||||||
{file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"},
|
|
||||||
{file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"},
|
|
||||||
{file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"},
|
|
||||||
{file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[extras]
|
[extras]
|
||||||
hiredis = ["hiredis"]
|
hiredis = ["hiredis"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.11"
|
python-versions = "^3.11"
|
||||||
content-hash = "3f863c00e5297f07e4691a38ef1b81c1428e114bbd9001d5340608cb3f6b70b2"
|
content-hash = "0da53315dee1155cfa3ec180a9120b32145497e290f13e17e481597179e2c993"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from importlib.metadata import version
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
|
@ -21,5 +23,6 @@ import uvicorn
|
||||||
),
|
),
|
||||||
type=int,
|
type=int,
|
||||||
)
|
)
|
||||||
|
@click.version_option(version("pssecret_server"))
|
||||||
def cli(**kwargs) -> None:
|
def cli(**kwargs) -> None:
|
||||||
uvicorn.run("pssecret_server.main:app", **kwargs)
|
uvicorn.run("pssecret_server.main:app", **kwargs)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from redis.asyncio import Redis
|
||||||
from pssecret_server.fernet import get_fernet
|
from pssecret_server.fernet import get_fernet
|
||||||
from pssecret_server.models import Secret, SecretSaveResult
|
from pssecret_server.models import Secret, SecretSaveResult
|
||||||
from pssecret_server.redis_db import get_redis
|
from pssecret_server.redis_db import get_redis
|
||||||
from pssecret_server.utils import decrypt_secret, encrypt_secret, save_secret
|
from pssecret_server.utils import decrypt_secret, encrypt_secret, getdel, save_secret
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ async def set_secret(
|
||||||
async def get_secret(
|
async def get_secret(
|
||||||
secret_key: str, redis: RedisDep, fernet: FernetDep
|
secret_key: str, redis: RedisDep, fernet: FernetDep
|
||||||
) -> dict[str, bytes]:
|
) -> dict[str, bytes]:
|
||||||
data: bytes | None = await redis.getdel(secret_key)
|
data: bytes | None = await getdel(redis, secret_key)
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
raise HTTPException(404)
|
raise HTTPException(404)
|
||||||
|
|
|
@ -2,7 +2,7 @@ from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
class Secret(BaseModel):
|
class Secret(BaseModel):
|
||||||
data: str = Field(title="Secret", description="Some secret data")
|
data: str = Field(title="Secret", description="Some secret data", min_length=1)
|
||||||
|
|
||||||
|
|
||||||
class SecretSaveResult(BaseModel):
|
class SecretSaveResult(BaseModel):
|
||||||
|
|
|
@ -3,7 +3,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
model_config = SettingsConfigDict(env_file=".env")
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
||||||
|
|
||||||
redis_url: RedisDsn = RedisDsn("redis://localhost")
|
redis_url: RedisDsn = RedisDsn("redis://localhost")
|
||||||
secrets_encryption_key: bytes
|
secrets_encryption_key: bytes
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
from functools import lru_cache
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
from redis.asyncio import Redis
|
from redis.asyncio import Redis
|
||||||
|
from redis.exceptions import ResponseError
|
||||||
|
from redis.typing import ResponseT
|
||||||
|
|
||||||
from pssecret_server.models import Secret
|
from pssecret_server.models import Secret
|
||||||
|
|
||||||
|
@ -30,3 +33,35 @@ async def save_secret(data: Secret, redis: Redis) -> str:
|
||||||
await redis.setex(new_key, 60 * 60 * 24, data.data)
|
await redis.setex(new_key, 60 * 60 * 24, data.data)
|
||||||
|
|
||||||
return new_key
|
return new_key
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
async def _is_getdel_available(redis: Redis) -> bool:
|
||||||
|
"""Checks the availability of GETDEL command on the Redis server instance
|
||||||
|
|
||||||
|
GETDEL is not available in Redis prior to version 6.2
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
await redis.getdel("test:getdel:availability")
|
||||||
|
except ResponseError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def getdel(redis: Redis, key: str) -> ResponseT:
|
||||||
|
"""Gets the value of key and deletes the key
|
||||||
|
|
||||||
|
Depending on the capabilities of Redis server this function
|
||||||
|
will either call GETDEL command, either first call GETSET with empty string
|
||||||
|
and DEL right after that.
|
||||||
|
"""
|
||||||
|
result: ResponseT
|
||||||
|
|
||||||
|
if await _is_getdel_available(redis):
|
||||||
|
result = await redis.getdel(key)
|
||||||
|
else:
|
||||||
|
result = await redis.getset(key, "")
|
||||||
|
await redis.delete(key)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pssecret-server"
|
name = "pssecret-server"
|
||||||
version = "0.0.1"
|
version = "1.1.2"
|
||||||
description = "API service for secrets sharing over network"
|
description = "API service for secrets sharing over network"
|
||||||
authors = ["Ivan Golikov <root@ivnglkv.me>"]
|
authors = ["Ivan Golikov <root@ivnglkv.me>"]
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
|
@ -9,12 +9,14 @@ homepage = "https://git.ivnglkv.me/root/pssecret-server"
|
||||||
repository = "https://git.ivnglkv.me/root/pssecret-server"
|
repository = "https://git.ivnglkv.me/root/pssecret-server"
|
||||||
documentation = "https://git.ivnglkv.me/root/pssecret-server/wiki"
|
documentation = "https://git.ivnglkv.me/root/pssecret-server/wiki"
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 2 - Pre-Alpha",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Environment :: Web Environment",
|
"Environment :: Web Environment",
|
||||||
"Framework :: FastAPI",
|
"Framework :: FastAPI",
|
||||||
"Intended Audience :: Information Technology",
|
"Intended Audience :: Information Technology",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application"
|
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -29,7 +31,6 @@ fastapi = { version = "0.115.6", extras = [ "standard" ] }
|
||||||
redis = "5.2.1"
|
redis = "5.2.1"
|
||||||
hiredis = { version = "3.1.0", optional = true }
|
hiredis = { version = "3.1.0", optional = true }
|
||||||
cryptography = "^44"
|
cryptography = "^44"
|
||||||
slowapi = "^0.1.9"
|
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
hiredis = ["hiredis"]
|
hiredis = ["hiredis"]
|
||||||
|
@ -52,3 +53,6 @@ reportUnusedCallResult = "none"
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
asyncio_mode = "auto"
|
asyncio_mode = "auto"
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
||||||
|
|
|
@ -3,6 +3,13 @@ from fastapi.testclient import TestClient
|
||||||
from tests.factories import SecretFactory
|
from tests.factories import SecretFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_secret_is_not_accepted(client: TestClient):
|
||||||
|
response = client.post("/secret", json={"data": ""})
|
||||||
|
|
||||||
|
assert response.status_code == 422
|
||||||
|
assert "data" in response.text
|
||||||
|
|
||||||
|
|
||||||
def test_store_secret_returns_key(client: TestClient):
|
def test_store_secret_returns_key(client: TestClient):
|
||||||
response = client.post("/secret", json=dict(SecretFactory().build()))
|
response = client.post("/secret", json=dict(SecretFactory().build()))
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from unittest.mock import patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
from redis.asyncio import Redis
|
from redis.asyncio import Redis
|
||||||
|
|
||||||
from pssecret_server.utils import get_new_key, save_secret
|
from pssecret_server.utils import get_new_key, getdel, save_secret
|
||||||
|
|
||||||
from ..factories import SecretFactory
|
from ..factories import SecretFactory
|
||||||
|
|
||||||
|
@ -33,3 +34,22 @@ async def test_save_secret_data(redis_server: Redis) -> None:
|
||||||
|
|
||||||
assert redis_data is not None
|
assert redis_data is not None
|
||||||
assert redis_data.decode() == secret.data
|
assert redis_data.decode() == secret.data
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("getdel_available", [True, False])
|
||||||
|
@patch("pssecret_server.utils._is_getdel_available", new_callable=AsyncMock)
|
||||||
|
async def test_getdel(
|
||||||
|
mock_is_getdel_available: AsyncMock,
|
||||||
|
getdel_available: bool,
|
||||||
|
redis_server: Redis,
|
||||||
|
) -> None:
|
||||||
|
mock_is_getdel_available.return_value = getdel_available
|
||||||
|
|
||||||
|
test_value = "test_data"
|
||||||
|
test_key = "test_key"
|
||||||
|
await redis_server.set(test_key, test_value)
|
||||||
|
|
||||||
|
result = await getdel(redis_server, test_key)
|
||||||
|
|
||||||
|
assert result.decode() == test_value # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
assert not await redis_server.exists(test_key)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from cryptography.fernet import Fernet, InvalidToken
|
from cryptography.fernet import Fernet, InvalidToken
|
||||||
|
from redis.exceptions import ResponseError
|
||||||
|
|
||||||
from pssecret_server.utils import decrypt_secret, encrypt_secret
|
from pssecret_server.utils import _is_getdel_available, decrypt_secret, encrypt_secret
|
||||||
|
|
||||||
from ..factories import SecretFactory
|
from ..factories import SecretFactory
|
||||||
|
|
||||||
|
@ -29,3 +32,17 @@ def test_secret_is_not_decryptable_by_random_key(fernet: Fernet):
|
||||||
|
|
||||||
with pytest.raises(InvalidToken):
|
with pytest.raises(InvalidToken):
|
||||||
decrypt_secret(encrypted_secret.data.encode(), random_fernet)
|
decrypt_secret(encrypted_secret.data.encode(), random_fernet)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("getdel_effect", "expected_result"), [(None, True), (ResponseError, False)]
|
||||||
|
)
|
||||||
|
async def test_is_getdel_available(
|
||||||
|
getdel_effect: ResponseError | None, expected_result: bool
|
||||||
|
):
|
||||||
|
redis = AsyncMock()
|
||||||
|
redis.getdel.side_effect = getdel_effect # pyright: ignore[reportAny]
|
||||||
|
|
||||||
|
result = await _is_getdel_available(redis)
|
||||||
|
|
||||||
|
assert result is expected_result
|
||||||
|
|
Loading…
Reference in a new issue