Compare commits

...

4 Commits

Author SHA1 Message Date
Oscar Krause
a12d05281c updated default data 2025-04-08 14:30:59 +02:00
Oscar Krause
a31c80465a code styling 2025-04-08 14:05:54 +02:00
Oscar Krause
ddf5f12409 fixes 2025-04-08 14:00:15 +02:00
Oscar Krause
20cdaefa1c code refactorings after merge from main 2025-04-08 13:52:09 +02:00
3 changed files with 44 additions and 53 deletions

View File

@ -1,8 +1,9 @@
import logging import logging
import sys
from base64 import b64encode as b64enc from base64 import b64encode as b64enc
from calendar import timegm from calendar import timegm
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from datetime import datetime, timedelta, UTC from datetime import datetime, UTC
from hashlib import sha256 from hashlib import sha256
from json import loads as json_loads from json import loads as json_loads
from os import getenv as env from os import getenv as env
@ -18,11 +19,9 @@ from jose.constants import ALGORITHMS
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from starlette.middleware.cors import CORSMiddleware from starlette.middleware.cors import CORSMiddleware
from starlette.responses import StreamingResponse, JSONResponse as JSONr, HTMLResponse as HTMLr, Response, \ from starlette.responses import StreamingResponse, JSONResponse as JSONr, HTMLResponse as HTMLr, Response, RedirectResponse
RedirectResponse
from orm import Origin, Lease, init as db_init, migrate from orm import Origin, Lease, init as db_init, migrate, Instance, Site
from util import PrivateKey, PublicKey, load_file
# Load variables # Load variables
load_dotenv('../version.env') load_dotenv('../version.env')
@ -249,7 +248,7 @@ async def _lease_delete(request: Request, lease_ref: str):
# venv/lib/python3.9/site-packages/nls_core_service_instance/service_instance_token_manager.py # venv/lib/python3.9/site-packages/nls_core_service_instance/service_instance_token_manager.py
@app.get('/-/client-token', summary='* Client-Token', description='creates a new messenger token for this service instance') @app.get('/-/client-token', summary='* Client-Token', description='creates a new messenger token for this service instance')
async def _client_token(): async def _client_token():
cur_time = datetime.utcnow() cur_time = datetime.now(UTC)
default_instance = Instance.get_default_instance(db) default_instance = Instance.get_default_instance(db)
public_key = default_instance.get_public_key() public_key = default_instance.get_public_key()
@ -281,10 +280,10 @@ async def _client_token():
}, },
"service_instance_public_key_configuration": { "service_instance_public_key_configuration": {
"service_instance_public_key_me": { "service_instance_public_key_me": {
"mod": hex(public_key.public_key().n)[2:], "mod": hex(public_key.raw().public_numbers().n)[2:],
"exp": int(public_key.public_key().e), "exp": int(public_key.raw().public_numbers().e),
}, },
"service_instance_public_key_pem": public_key.export_key().decode('utf-8'), "service_instance_public_key_pem": public_key.pem().decode('utf-8'),
"key_retention_mode": "LATEST_ONLY" "key_retention_mode": "LATEST_ONLY"
}, },
} }
@ -399,7 +398,7 @@ async def auth_v1_token(request: Request):
jwt_encode_key, jwt_decode_key = default_instance.get_jwt_encode_key(), default_instance.get_jwt_decode_key() jwt_encode_key, jwt_decode_key = default_instance.get_jwt_encode_key(), default_instance.get_jwt_decode_key()
try: try:
payload = jwt.decode(token=j.get('auth_code'), key=jwt_decode_key, algorithms=[ALGORITHMS.RS256]) payload = jwt.decode(token=j.get('auth_code'), key=jwt_decode_key, algorithms=ALGORITHMS.RS256)
except JWTError as e: except JWTError as e:
return JSONr(status_code=400, content={'status': 400, 'title': 'invalid token', 'detail': str(e)}) return JSONr(status_code=400, content={'status': 400, 'title': 'invalid token', 'detail': str(e)})
@ -438,7 +437,7 @@ async def auth_v1_token(request: Request):
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py # venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
@app.post('/leasing/v1/lessor', description='request multiple leases (borrow) for current origin') @app.post('/leasing/v1/lessor', description='request multiple leases (borrow) for current origin')
async def leasing_v1_lessor(request: Request): async def leasing_v1_lessor(request: Request):
j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.utcnow() j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC)
default_instance = Instance.get_default_instance(db) default_instance = Instance.get_default_instance(db)
jwt_decode_key = default_instance.get_jwt_decode_key() jwt_decode_key = default_instance.get_jwt_decode_key()
@ -489,7 +488,7 @@ async def leasing_v1_lessor(request: Request):
# venv/lib/python3.9/site-packages/nls_dal_service_instance_dls/schema/service_instance/V1_0_21__product_mapping.sql # venv/lib/python3.9/site-packages/nls_dal_service_instance_dls/schema/service_instance/V1_0_21__product_mapping.sql
@app.get('/leasing/v1/lessor/leases', description='get active leases for current origin') @app.get('/leasing/v1/lessor/leases', description='get active leases for current origin')
async def leasing_v1_lessor_lease(request: Request): async def leasing_v1_lessor_lease(request: Request):
cur_time = datetime.utcnow() cur_time = datetime.now(UTC)
jwt_decode_key = Instance.get_default_instance(db).get_jwt_decode_key() jwt_decode_key = Instance.get_default_instance(db).get_jwt_decode_key()
@ -516,7 +515,7 @@ async def leasing_v1_lessor_lease(request: Request):
# venv/lib/python3.9/site-packages/nls_core_lease/lease_single.py # venv/lib/python3.9/site-packages/nls_core_lease/lease_single.py
@app.put('/leasing/v1/lease/{lease_ref}', description='renew a lease') @app.put('/leasing/v1/lease/{lease_ref}', description='renew a lease')
async def leasing_v1_lease_renew(request: Request, lease_ref: str): async def leasing_v1_lease_renew(request: Request, lease_ref: str):
cur_time = datetime.utcnow() cur_time = datetime.now(UTC)
default_instance = Instance.get_default_instance(db) default_instance = Instance.get_default_instance(db)
jwt_decode_key = default_instance.get_jwt_decode_key() jwt_decode_key = default_instance.get_jwt_decode_key()
@ -551,7 +550,7 @@ async def leasing_v1_lease_renew(request: Request, lease_ref: str):
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_single_controller.py # venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_single_controller.py
@app.delete('/leasing/v1/lease/{lease_ref}', description='release (return) a lease') @app.delete('/leasing/v1/lease/{lease_ref}', description='release (return) a lease')
async def leasing_v1_lease_delete(request: Request, lease_ref: str): async def leasing_v1_lease_delete(request: Request, lease_ref: str):
cur_time = datetime.utcnow() cur_time = datetime.now(UTC)
jwt_decode_key = Instance.get_default_instance(db).get_jwt_decode_key() jwt_decode_key = Instance.get_default_instance(db).get_jwt_decode_key()
@ -584,7 +583,7 @@ async def leasing_v1_lease_delete(request: Request, lease_ref: str):
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py # venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
@app.delete('/leasing/v1/lessor/leases', description='release all leases') @app.delete('/leasing/v1/lessor/leases', description='release all leases')
async def leasing_v1_lessor_lease_remove(request: Request): async def leasing_v1_lessor_lease_remove(request: Request):
cur_time = datetime.utcnow() cur_time = datetime.now(UTC)
jwt_decode_key = Instance.get_default_instance(db).get_jwt_decode_key() jwt_decode_key = Instance.get_default_instance(db).get_jwt_decode_key()

View File

@ -1,11 +1,17 @@
import logging
from datetime import datetime, timedelta, timezone, UTC from datetime import datetime, timedelta, timezone, UTC
from os import getenv as env
from os.path import join, dirname, isfile
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_, inspect, text from jose import jwk
from jose.constants import ALGORITHMS
from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_, inspect, text, BLOB, INT, FLOAT
from sqlalchemy.engine import Engine from sqlalchemy.engine import Engine
from sqlalchemy.orm import sessionmaker, declarative_base, Session, relationship from sqlalchemy.orm import sessionmaker, declarative_base, Session, relationship
from sqlalchemy.schema import CreateTable
from util import NV from util import NV, PrivateKey, PublicKey
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -17,8 +23,8 @@ Base = declarative_base()
class Site(Base): class Site(Base):
__tablename__ = "site" __tablename__ = "site"
INITIAL_SITE_KEY_XID = '00000000-0000-0000-0000-000000000000' INITIAL_SITE_KEY_XID = '10000000-0000-0000-0000-000000000000'
INITIAL_SITE_NAME = 'default' INITIAL_SITE_NAME = 'default-site'
site_key = Column(CHAR(length=36), primary_key=True, unique=True, index=True) # uuid4, SITE_KEY_XID site_key = Column(CHAR(length=36), primary_key=True, unique=True, index=True) # uuid4, SITE_KEY_XID
name = Column(VARCHAR(length=256), nullable=False) name = Column(VARCHAR(length=256), nullable=False)
@ -28,7 +34,6 @@ class Site(Base):
@staticmethod @staticmethod
def create_statement(engine: Engine): def create_statement(engine: Engine):
from sqlalchemy.schema import CreateTable
return CreateTable(Site.__table__).compile(engine) return CreateTable(Site.__table__).compile(engine)
@staticmethod @staticmethod
@ -65,7 +70,6 @@ class Instance(Base):
@staticmethod @staticmethod
def create_statement(engine: Engine): def create_statement(engine: Engine):
from sqlalchemy.schema import CreateTable
return CreateTable(Instance.__table__).compile(engine) return CreateTable(Instance.__table__).compile(engine)
@staticmethod @staticmethod
@ -111,21 +115,17 @@ class Instance(Base):
def get_client_token_expire_delta(self) -> "dateutil.relativedelta.relativedelta": def get_client_token_expire_delta(self) -> "dateutil.relativedelta.relativedelta":
return relativedelta(seconds=self.client_token_expire_delta) return relativedelta(seconds=self.client_token_expire_delta)
def __get_private_key(self) -> "RsaKey": def __get_private_key(self) -> "PrivateKey":
return parse_key(self.private_key) return PrivateKey(self.private_key)
def get_public_key(self) -> "RsaKey": def get_public_key(self) -> "PublicKey":
return parse_key(self.public_key) return PublicKey(self.public_key)
def get_jwt_encode_key(self) -> "jose.jkw": def get_jwt_encode_key(self) -> "jose.jkw":
from jose import jwk return jwk.construct(self.__get_private_key().pem().decode('utf-8'), algorithm=ALGORITHMS.RS256)
from jose.constants import ALGORITHMS
return jwk.construct(self.__get_private_key().export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
def get_jwt_decode_key(self) -> "jose.jwt": def get_jwt_decode_key(self) -> "jose.jwt":
from jose import jwk return jwk.construct(self.get_public_key().pem().decode('utf-8'), algorithm=ALGORITHMS.RS256)
from jose.constants import ALGORITHMS
return jwk.construct(self.get_public_key().export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
def get_private_key_str(self, encoding: str = 'utf-8') -> str: def get_private_key_str(self, encoding: str = 'utf-8') -> str:
return self.private_key.decode(encoding) return self.private_key.decode(encoding)
@ -162,7 +162,6 @@ class Origin(Base):
@staticmethod @staticmethod
def create_statement(engine: Engine): def create_statement(engine: Engine):
from sqlalchemy.schema import CreateTable
return CreateTable(Origin.__table__).compile(engine) return CreateTable(Origin.__table__).compile(engine)
@staticmethod @staticmethod
@ -241,7 +240,6 @@ class Lease(Base):
@staticmethod @staticmethod
def create_statement(engine: Engine): def create_statement(engine: Engine):
from sqlalchemy.schema import CreateTable
return CreateTable(Lease.__table__).compile(engine) return CreateTable(Lease.__table__).compile(engine)
@staticmethod @staticmethod
@ -336,9 +334,7 @@ class Lease(Base):
def init_default_site(session: Session): def init_default_site(session: Session):
from app.util import generate_key private_key = PrivateKey.generate()
private_key = generate_key()
public_key = private_key.public_key() public_key = private_key.public_key()
site = Site( site = Site(
@ -351,8 +347,8 @@ def init_default_site(session: Session):
instance = Instance( instance = Instance(
instance_ref=Instance.DEFAULT_INSTANCE_REF, instance_ref=Instance.DEFAULT_INSTANCE_REF,
site_key=site.site_key, site_key=site.site_key,
private_key=private_key.export_key(), private_key=private_key.pem(),
public_key=public_key.export_key(), public_key=public_key.pem(),
) )
session.add(instance) session.add(instance)
session.commit() session.commit()
@ -379,10 +375,6 @@ def init(engine: Engine):
def migrate(engine: Engine): def migrate(engine: Engine):
from os import getenv as env
from os.path import join, dirname, isfile
from util import load_key
db = inspect(engine) db = inspect(engine)
# todo: add update guide to use 1.LATEST to 2.0 # todo: add update guide to use 1.LATEST to 2.0
@ -408,15 +400,15 @@ def migrate(engine: Engine):
default_instance_private_key_path = str(join(dirname(__file__), 'cert/instance.private.pem')) default_instance_private_key_path = str(join(dirname(__file__), 'cert/instance.private.pem'))
instance_private_key = env('INSTANCE_KEY_RSA', None) instance_private_key = env('INSTANCE_KEY_RSA', None)
if instance_private_key is not None: if instance_private_key is not None:
instance.private_key = load_key(str(instance_private_key)) instance.private_key = PrivateKey(instance_private_key.encode('utf-8'))
elif isfile(default_instance_private_key_path): elif isfile(default_instance_private_key_path):
instance.private_key = load_key(default_instance_private_key_path) instance.private_key = PrivateKey.from_file(default_instance_private_key_path)
default_instance_public_key_path = str(join(dirname(__file__), 'cert/instance.public.pem')) default_instance_public_key_path = str(join(dirname(__file__), 'cert/instance.public.pem'))
instance_public_key = env('INSTANCE_KEY_PUB', None) instance_public_key = env('INSTANCE_KEY_PUB', None)
if instance_public_key is not None: if instance_public_key is not None:
instance.public_key = load_key(str(instance_public_key)) instance.public_key = PublicKey(instance_public_key.encode('utf-8'))
elif isfile(default_instance_public_key_path): elif isfile(default_instance_public_key_path):
instance.public_key = load_key(default_instance_public_key_path) instance.public_key = PublicKey.from_file(default_instance_public_key_path)
# TOKEN_EXPIRE_DELTA # TOKEN_EXPIRE_DELTA
token_expire_delta = env('TOKEN_EXPIRE_DAYS', None) token_expire_delta = env('TOKEN_EXPIRE_DAYS', None)

View File

@ -1,23 +1,23 @@
from os import getenv as env import sys
from base64 import b64encode as b64enc from base64 import b64encode as b64enc
from hashlib import sha256
from calendar import timegm from calendar import timegm
from datetime import datetime from datetime import datetime, UTC
from uuid import UUID, uuid4 from hashlib import sha256
from os import getenv as env
from uuid import uuid4, UUID
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from jose import jwt from jose import jwt
from jose.constants import ALGORITHMS from jose.constants import ALGORITHMS
from starlette.testclient import TestClient
from sqlalchemy import create_engine from sqlalchemy import create_engine
import sys from starlette.testclient import TestClient
# add relative path to use packages as they were in the app/ dir # add relative path to use packages as they were in the app/ dir
sys.path.append('../') sys.path.append('../')
sys.path.append('../app') sys.path.append('../app')
from app import main from app import main
from app.orm import init as db_init, migrate, Site, Instance from orm import init as db_init, migrate, Site, Instance
ORIGIN_REF, ALLOTMENT_REF, SECRET = str(uuid4()), '20000000-0000-0000-0000-000000000001', 'HelloWorld' ORIGIN_REF, ALLOTMENT_REF, SECRET = str(uuid4()), '20000000-0000-0000-0000-000000000001', 'HelloWorld'