This commit is contained in:
AF 2023-10-02 17:00:56 +00:00
parent 9297358dff
commit 5277e2cb85
12 changed files with 102 additions and 76 deletions

View File

@ -1,21 +1,21 @@
from setuptools import setup
setup(
name='v6d0auth',
version='',
packages=['v6d0auth'],
url='',
license='',
author='PARRRATE TNV',
author_email='',
description='',
name="v6d0auth",
version="",
packages=["v6d0auth"],
url="",
license="",
author="PARRRATE TNV",
author_email="",
description="",
install_requires=[
'aiohttp',
'PyNaCl~=1.4.0',
"aiohttp",
"PyNaCl~=1.4.0",
],
extras_require={
'full': [
'ptvp35 @ git+https://gitea.parrrate.ru/PTV/ptvp35.git@e760fca39e2070b9959aeb95b53e59e895f1ad57',
"full": [
"ptvp35 @ git+https://gitea.parrrate.ru/PTV/ptvp35.git@e760fca39e2070b9959aeb95b53e59e895f1ad57",
],
},
)

View File

@ -10,7 +10,7 @@ from v6d0auth import certs
from v6d0auth.appfactory import *
from v6d0auth.cdb import *
__all__ = ('V6D0AuthAppFactory',)
__all__ = ("V6D0AuthAppFactory",)
class V6D0AuthAppFactory(AppFactory):
@ -21,26 +21,26 @@ class V6D0AuthAppFactory(AppFactory):
print(certs.vkey.encode().hex())
self.cdb.start()
@routes.get('/')
@routes.get("/")
async def home(_request: web.Request):
return web.Response(body='v6d0auth\n')
return web.Response(body="v6d0auth\n")
async def ws_approve(ws: web.WebSocketResponse):
nonce = random(16)
await ws.send_bytes(nonce)
hhandle, hnonce = json.loads(certs.verify(await ws.receive_bytes()))
assert hnonce == nonce.hex()
if hhandle == 'all':
print('approving all')
if hhandle == "all":
print("approving all")
for request in self.cdb.handle_mapping.values():
request.approve()
print('approved all')
print("approved all")
else:
approved = self.cdb.approve(bytes.fromhex(hhandle))
await ws.send_bytes(approved)
await ws.close()
@routes.get('/approve')
@routes.get("/approve")
async def approve(request: web.Request):
ws = web.WebSocketResponse()
await ws.prepare(request)
@ -51,7 +51,7 @@ class V6D0AuthAppFactory(AppFactory):
return VerifyKey(await request.read())
def role_for_request(request: web.Request) -> Optional[str]:
return request.headers.get('v6role')
return request.headers.get("v6role")
def pushed_for_role(requester: VerifyKey, role: Optional[str]) -> AbstractRequest:
if role is None:
@ -62,7 +62,7 @@ class V6D0AuthAppFactory(AppFactory):
async def pushed_for_request(request: web.Request) -> AbstractRequest:
return pushed_for_role(await requester_for_request(request), role_for_request(request))
@routes.post('/push')
@routes.post("/push")
async def push(request: web.Request):
pushed = await pushed_for_request(request)
timeout = pushed.timeout
@ -77,7 +77,7 @@ class V6D0AuthAppFactory(AppFactory):
async def pulled_for_request(request: web.Request) -> Optional[bytes]:
return pulled_for_role(await requester_for_request(request), role_for_request(request))
@routes.post('/pull')
@routes.post("/pull")
async def pull(request: web.Request):
try:
pulled = await pulled_for_request(request)
@ -86,13 +86,13 @@ class V6D0AuthAppFactory(AppFactory):
else:
return web.Response(body=pulled)
@routes.post('/has_role')
@routes.post("/has_role")
async def has_role(request: web.Request):
role = role_for_request(request)
if role is None:
raise web.HTTPBadRequest
return web.Response(
body=(b'1' if self.cdb.has_role(Role(await requester_for_request(request), role)) else b'')
body=(b"1" if self.cdb.has_role(Role(await requester_for_request(request), role)) else b"")
)
async def ws_remove(ws: web.WebSocketResponse):
@ -101,10 +101,10 @@ class V6D0AuthAppFactory(AppFactory):
[hrequester, role], hnonce = json.loads(certs.verify(await ws.receive_bytes()))
assert hnonce == nonce.hex()
self.cdb.remove_role(Role(VerifyKey(bytes.fromhex(hrequester)), role))
await ws.send_bytes(b'0')
await ws.send_bytes(b"0")
await ws.close()
@routes.get('/remove_role')
@routes.get("/remove_role")
async def remove_role(request: web.Request):
ws = web.WebSocketResponse()
await ws.prepare(request)
@ -147,7 +147,7 @@ class V6D0AuthAppFactory(AppFactory):
else:
await srq_process(ws, srq)
@routes.get('/pullws')
@routes.get("/pullws")
async def pullws(request: web.Request):
ws = web.WebSocketResponse()
await ws.prepare(request)

View File

@ -1,6 +1,6 @@
from aiohttp import web
__all__ = ('AppFactory',)
__all__ = ("AppFactory",)
class AppFactory:

View File

@ -13,7 +13,11 @@ from ptvp35 import Db, KVJson
from v6d0auth import certs
from v6d0auth.config import myroot
__all__ = ('CDB', 'Role', 'AbstractRequest',)
__all__ = (
"CDB",
"Role",
"AbstractRequest",
)
TIMEOUT = 300
@ -60,15 +64,15 @@ class AbstractRequest:
return approved
self._approved = self._approve()
self.future.set_result(self._approved)
print('validating', self.handle.hex())
print("validating", self.handle.hex())
self.validate()
print('approved', self.handle.hex())
print("approved", self.handle.hex())
return self._approved
def repair(self):
if self.future.done():
self.future: asyncio.Future[bytes] = self._loop.create_future()
print('repaired', self.handle.hex(), self.display())
print("repaired", self.handle.hex(), self.display())
def force_repair(self):
if not self.future.done():
@ -131,7 +135,7 @@ class Role(Hashable):
return NotImplemented
def display(self):
return f'{self._requester.encode().hex()}@{self._role}'
return f"{self._requester.encode().hex()}@{self._role}"
class RoleRequest(AbstractRequest):
@ -142,7 +146,7 @@ class RoleRequest(AbstractRequest):
def _approve(self) -> bytes:
self._rdb.set_nowait(self._role.key(), True)
return b'1'
return b"1"
def _validate(self) -> None:
assert self._rdb.get(self._role.key(), False)
@ -151,7 +155,7 @@ class RoleRequest(AbstractRequest):
return self._role.display()
_rdbfile = myroot / 'roles.db'
_rdbfile = myroot / "roles.db"
class CDB:
@ -171,14 +175,14 @@ class CDB:
def cleanup(self):
if self.heap:
print('cleaning up')
print("cleaning up")
for request in self._cleanup():
print('cleaned', request.handle.hex())
print("cleaned", request.handle.hex())
def push_abstract(self, request: AbstractRequest):
heapq.heappush(self.heap, request)
self.handle_mapping[request.handle] = request
print('requested', request.handle.hex(), request.display())
print("requested", request.handle.hex(), request.display())
def push_requester(self, requester: VerifyKey) -> SignatureRequest:
if requester in self.requester_mapping:

View File

@ -5,9 +5,16 @@ from nacl.signing import SigningKey, VerifyKey, SignedMessage
from v6d0auth.config import myroot, cakey
__all__ = ('vkey', 'pkey', 'sign', 'averify', 'receive', 'encrypt_self',)
__all__ = (
"vkey",
"pkey",
"sign",
"averify",
"receive",
"encrypt_self",
)
_keyfile = myroot / '.key'
_keyfile = myroot / ".key"
if _keyfile.exists():
_skey = SigningKey(_keyfile.read_bytes())
else:

View File

@ -5,15 +5,21 @@ from nacl.signing import VerifyKey
from v6d0auth import certs
from v6d0auth.config import caurl, myroot
__all__ = ('request_signature', 'mycert', 'has_role', 'request_role', 'with_role',)
__all__ = (
"request_signature",
"mycert",
"has_role",
"request_role",
"with_role",
)
async def request_signature() -> bytes:
async with aiohttp.ClientSession() as session:
async with session.post(f'{caurl}/push', data=certs.vkey.encode()) as response:
async with session.post(f"{caurl}/push", data=certs.vkey.encode()) as response:
if response.status not in [200, 429]:
raise RuntimeError(response.status)
async with session.ws_connect(f'{caurl}/pullws') as ws:
async with session.ws_connect(f"{caurl}/pullws") as ws:
await ws.send_bytes(certs.vkey.encode())
try:
return await ws.receive_bytes()
@ -21,7 +27,7 @@ async def request_signature() -> bytes:
raise RuntimeError("signature request failed") from e
_certfile = myroot / 'cert'
_certfile = myroot / "cert"
async def mycert() -> bytes:
@ -36,16 +42,16 @@ async def mycert() -> bytes:
async def has_role(vkey: VerifyKey, role: str):
async with aiohttp.ClientSession() as session:
async with session.post(f'{caurl}/has_role', data=vkey.encode(), headers={'v6role': role}) as response:
return (await response.read()) == b'1'
async with session.post(f"{caurl}/has_role", data=vkey.encode(), headers={"v6role": role}) as response:
return (await response.read()) == b"1"
async def request_role(role: str) -> bytes:
async with aiohttp.ClientSession() as session:
async with session.post(f'{caurl}/push', data=certs.vkey.encode(), headers={'v6role': role}) as response:
async with session.post(f"{caurl}/push", data=certs.vkey.encode(), headers={"v6role": role}) as response:
if response.status not in [200, 429]:
raise RuntimeError(response.status)
async with session.ws_connect(f'{caurl}/pullws', headers={'v6role': role}) as ws:
async with session.ws_connect(f"{caurl}/pullws", headers={"v6role": role}) as ws:
await ws.send_bytes(certs.vkey.encode())
try:
return await ws.receive_bytes()

View File

@ -1,13 +1,20 @@
import os
from pathlib import Path
__all__ = ('root', 'myroot', 'host', 'port', 'cakey', 'caurl',)
__all__ = (
"root",
"myroot",
"host",
"port",
"cakey",
"caurl",
)
root = Path(os.getenv('v6root', './data'))
root = Path(os.getenv("v6root", "./data"))
assert root.exists()
myroot = root / 'v6d0auth'
myroot = root / "v6d0auth"
myroot.mkdir(exist_ok=True)
host = os.getenv('v6host', '127.0.0.1')
port = int(os.getenv('v6port', '5003'))
cakey = bytes.fromhex(os.getenv('v6ca', ''))
caurl = os.getenv('v6caurl', f'http://127.0.0.1:{port}')
host = os.getenv("v6host", "127.0.0.1")
port = int(os.getenv("v6port", "5003"))
cakey = bytes.fromhex(os.getenv("v6ca", ""))
caurl = os.getenv("v6caurl", f"http://127.0.0.1:{port}")

View File

@ -8,8 +8,8 @@ from v6d0auth import certs
from v6d0auth.config import host, port
parser = argparse.ArgumentParser()
parser.add_argument('requester', type=str)
parser.add_argument('role', type=str)
parser.add_argument("requester", type=str)
parser.add_argument("role", type=str)
async def main():
@ -17,12 +17,12 @@ async def main():
role = args.role
async with aiohttp.ClientSession() as session:
# noinspection HttpUrlsUsage
async with session.ws_connect(f'http://{host}:{port}/remove_role') as ws:
async with session.ws_connect(f"http://{host}:{port}/remove_role") as ws:
nonce = await ws.receive_bytes()
await ws.send_bytes(certs.sign(json.dumps([[requester.hex(), role], nonce.hex()]).encode()))
print((await ws.receive_bytes()).hex())
if __name__ == '__main__':
if __name__ == "__main__":
args = parser.parse_args()
asyncio.run(main())

View File

@ -11,7 +11,7 @@ async def main():
await run_app(V6D0AuthAppFactory(cdb).app())
if __name__ == '__main__':
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:

View File

@ -5,7 +5,10 @@ from aiohttp import web
from v6d0auth.config import host, port
__all__ = ('start_app', 'run_app',)
__all__ = (
"start_app",
"run_app",
)
async def start_app(app: web.Application, keepalive_timeout=75.0):
@ -17,10 +20,7 @@ async def start_app(app: web.Application, keepalive_timeout=75.0):
site = web.TCPSite(runner, host=host, port=port)
await site.start()
names = sorted(str(s.name) for s in runner.sites)
print(
"======== Running on {} ========\n"
"(Press CTRL+C to quit)".format(", ".join(names))
)
print("======== Running on {} ========\n" "(Press CTRL+C to quit)".format(", ".join(names)))
loop = asyncio.get_running_loop()

View File

@ -9,20 +9,22 @@ from v6d0auth import certs
from v6d0auth.config import host, port
parser = argparse.ArgumentParser()
parser.add_argument('handle', type=str)
parser.add_argument("handle", type=str)
async def main():
handle: bytes | Literal['all'] = 'all' if args.handle == 'all' else bytes.fromhex(args.handle)
handle: bytes | Literal["all"] = "all" if args.handle == "all" else bytes.fromhex(args.handle)
async with aiohttp.ClientSession() as session:
# noinspection HttpUrlsUsage
async with session.ws_connect(f'http://{host}:{port}/approve') as ws:
async with session.ws_connect(f"http://{host}:{port}/approve") as ws:
nonce = await ws.receive_bytes()
await ws.send_bytes(certs.sign(json.dumps(['all' if handle == 'all' else handle.hex(), nonce.hex()]).encode()))
if handle != 'all':
await ws.send_bytes(
certs.sign(json.dumps(["all" if handle == "all" else handle.hex(), nonce.hex()]).encode())
)
if handle != "all":
print((await ws.receive_bytes()).hex())
if __name__ == '__main__':
if __name__ == "__main__":
args = parser.parse_args()
asyncio.run(main())

View File

@ -9,11 +9,11 @@ from v6d0auth.client import *
async def main():
print(certs.vkey.encode().hex())
print((await request_signature()).hex())
call([executable, '-m', 'v6d0auth.remove-role', certs.vkey.encode().hex(), 'test'])
print(await has_role(certs.vkey, 'test'))
print(await request_role('test'))
print(await has_role(certs.vkey, 'test'))
call([executable, "-m", "v6d0auth.remove-role", certs.vkey.encode().hex(), "test"])
print(await has_role(certs.vkey, "test"))
print(await request_role("test"))
print(await has_role(certs.vkey, "test"))
if __name__ == '__main__':
if __name__ == "__main__":
asyncio.run(main())