mirror of
				https://git.collinwebdesigns.de/oscar.krause/fastapi-dls.git
				synced 2025-10-26 18:05:28 +03:00 
			
		
		
		
	Merge branch 'dev' into 'main'
1.2 See merge request oscar.krause/fastapi-dls!16
This commit is contained in:
		
						commit
						c894537ff9
					
				| @ -6,10 +6,12 @@ CONFIG_DIR=/etc/fastapi-dls | ||||
| echo "> Create config directory ..." | ||||
| mkdir -p $CONFIG_DIR | ||||
| 
 | ||||
| # normally we would define services in `conffiles` and as separate file, but we like to keep thinks simple. | ||||
| echo "> Install service ..." | ||||
| cat <<EOF >/etc/systemd/system/fastapi-dls.service | ||||
| [Unit] | ||||
| Description=Service for fastapi-dls | ||||
| Documentation=https://git.collinwebdesigns.de/oscar.krause/fastapi-dls | ||||
| After=network.target | ||||
| 
 | ||||
| [Service] | ||||
| @ -37,6 +39,7 @@ EOF | ||||
| 
 | ||||
| systemctl daemon-reload | ||||
| 
 | ||||
| # normally we would define configfiles in `conffiles` and as separate file, but we like to keep thinks simple. | ||||
| if [[ ! -f $CONFIG_DIR/env ]]; then | ||||
|   echo "> Writing initial config ..." | ||||
|   touch $CONFIG_DIR/env | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| # Maintainer: samicrusader <hi@samicrusader.me> | ||||
| # Maintainer: Oscar Krause <oscar.krause@collinwebdesigns.de> | ||||
| # Contributor: samicrusader <hi@samicrusader.me> | ||||
| 
 | ||||
| pkgname=fastapi-dls | ||||
| pkgver=0.0 | ||||
| pkgver=1.1 | ||||
| pkgrel=1 | ||||
| pkgdesc='NVIDIA DLS server implementation with FastAPI' | ||||
| arch=('any') | ||||
| @ -13,10 +13,12 @@ provider=("$pkgname") | ||||
| install="$pkgname.install" | ||||
| source=('git+file:///builds/oscar.krause/fastapi-dls' # https://gitea.publichub.eu/oscar.krause/fastapi-dls.git | ||||
|         "$pkgname.default" | ||||
|         "$pkgname.service") | ||||
|         "$pkgname.service" | ||||
|         "$pkgname.tmpfiles") | ||||
| sha256sums=('SKIP' | ||||
|             '4c07e9b627853bd4f3a398371912fc72302dac33f43e4cb7e9b79746cc9c9136' | ||||
|             '10cb98d64f8bf37b11a60510793c187cc664e63c895d1205781c21fa2e703f32') | ||||
|             'fbd015449a30c0ae82733289a56eb98151dcfab66c91b37fe8e202e39f7a5edb' | ||||
|             '2719338541104c537453a65261c012dda58e1dbee99154cf4f33b526ee6ca22e' | ||||
|             '3dc60140c08122a8ec0e7fa7f0937eb8c1288058890ba09478420fc30ce9e30c') | ||||
| 
 | ||||
| pkgver() { | ||||
|   source $srcdir/$pkgname/version.env | ||||
| @ -46,4 +48,5 @@ package() { | ||||
|     install -Dm755 "$srcdir/$pkgname/app/util.py" "$pkgdir/opt/$pkgname/util.py" | ||||
|     install -Dm644 "$srcdir/$pkgname.default" "$pkgdir/etc/default/$pkgname" | ||||
|     install -Dm644 "$srcdir/$pkgname.service" "$pkgdir/usr/lib/systemd/system/$pkgname.service" | ||||
|     install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf" | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ DEBUG=false | ||||
| 
 | ||||
| # Where the client can find the DLS server | ||||
| ## DLS_URL should be a hostname | ||||
| LISTEN_IP="0.0.0.0" | ||||
| DLS_URL="localhost.localdomain" | ||||
| DLS_PORT=8443 | ||||
| CORS_ORIGINS="https://$DLS_URL:$DLS_PORT" | ||||
| @ -21,3 +22,7 @@ INSTANCE_REF="<<instanceref>>" | ||||
| # Site-wide signing keys | ||||
| INSTANCE_KEY_RSA="/var/lib/fastapi-dls/instance.private.pem" | ||||
| INSTANCE_KEY_PUB="/var/lib/fastapi-dls/instance.public.pem" | ||||
| 
 | ||||
| # TLS certificate | ||||
| INSTANCE_SSL_CERT="/var/lib/fastapi-dls/cert/webserver.crt" | ||||
| INSTANCE_SSL_KEY="/var/lib/fastapi-dls/cert/webserver.key" | ||||
|  | ||||
| @ -4,12 +4,13 @@ Documentation=https://git.collinwebdesigns.de/oscar.krause/fastapi-dls | ||||
| After=network.target | ||||
| 
 | ||||
| [Service] | ||||
| Type=forking | ||||
| Type=simple | ||||
| AmbientCapabilities=CAP_NET_BIND_SERVICE | ||||
| EnvironmentFile=/etc/default/fastapi-dls | ||||
| ExecStart=/usr/bin/python /opt/fastapi-dls/main.py | ||||
| WorkingDir=/opt/fastapi-dls | ||||
| ExecStart=/usr/bin/uvicorn main:app --proxy-headers --env-file=/etc/default/fastapi-dls --host=${LISTEN_IP} --port=${DLS_PORT} --app-dir=/opt/fastapi-dls --ssl-keyfile=${INSTANCE_SSL_KEY} --ssl-certfile=${INSTANCE_SSL_CERT} | ||||
| Restart=on-abort | ||||
| User=root | ||||
| User=http | ||||
| Group=http | ||||
| 
 | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										2
									
								
								.PKGBUILD/fastapi-dls.tmpfiles
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.PKGBUILD/fastapi-dls.tmpfiles
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| d /var/lib/fastapi-dls 0755 http http | ||||
| d /var/lib/fastapi-dls/cert 0755 http http | ||||
| @ -275,9 +275,12 @@ release: | ||||
|       when: never | ||||
|     - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH | ||||
|   before_script: | ||||
|     - set -a # make variables from "source" command available to release-cli | ||||
|     - source version.env | ||||
|   script: | ||||
|     - echo "Running release-job for $VERSION" | ||||
|   after_script: | ||||
|     - set +a | ||||
|   release: | ||||
|     name: $CI_PROJECT_TITLE $version | ||||
|     description: Release of $CI_PROJECT_TITLE version $VERSION | ||||
|  | ||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @ -287,12 +287,14 @@ After first success you have to replace `--issue` with `--renew`. | ||||
| | `DLS_PORT`          | `443`                                  | Used in client-token to tell guest driver where dls instance is reachable           | | ||||
| | `LEASE_EXPIRE_DAYS` | `90`                                   | Lease time in days                                                                  | | ||||
| | `DATABASE`          | `sqlite:///db.sqlite`                  | See [official SQLAlchemy docs](https://docs.sqlalchemy.org/en/14/core/engines.html) | | ||||
| | `CORS_ORIGINS`      | `https://{DLS_URL}`                    | Sets `Access-Control-Allow-Origin` header (comma separated string)                  | | ||||
| | `CORS_ORIGINS`      | `https://{DLS_URL}`                    | Sets `Access-Control-Allow-Origin` header (comma separated string) \*               | | ||||
| | `SITE_KEY_XID`      | `00000000-0000-0000-0000-000000000000` | Site identification uuid                                                            | | ||||
| | `INSTANCE_REF`      | `00000000-0000-0000-0000-000000000000` | Instance identification uuid                                                        | | ||||
| | `INSTANCE_KEY_RSA`  | `<app-dir>/cert/instance.private.pem`  | Site-wide private RSA key for singing JWTs                                          | | ||||
| | `INSTANCE_KEY_PUB`  | `<app-dir>/cert/instance.public.pem`   | Site-wide public key                                                                | | ||||
| 
 | ||||
| \* Always use `https`, since guest-drivers only support secure connections! | ||||
| 
 | ||||
| # Setup (Client) | ||||
| 
 | ||||
| **The token file has to be copied! It's not enough to C&P file contents, because there can be special characters.** | ||||
| @ -316,6 +318,14 @@ nvidia-smi -q | grep "License" | ||||
| Download file and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`. | ||||
| Now restart `NvContainerLocalSystem` service. | ||||
| 
 | ||||
| **Power-Shell** | ||||
| 
 | ||||
| ```Shell | ||||
| curl.exe --insecure -X GET https://<dls-hostname-or-ip>/client-token -o "C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken\client_configuration_token_$($(Get-Date).tostring('dd-MM-yy-hh-mm-ss')).tok" | ||||
| Restart-Service NVDisplay.ContainerLocalSystem | ||||
| 'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe' -q | Select-String "License" | ||||
| ``` | ||||
| 
 | ||||
| # Troubleshoot | ||||
| 
 | ||||
| ## Linux | ||||
|  | ||||
							
								
								
									
										90
									
								
								app/main.py
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								app/main.py
									
									
									
									
									
								
							| @ -40,8 +40,7 @@ INSTANCE_KEY_RSA = load_key(str(env('INSTANCE_KEY_RSA', join(dirname(__file__), | ||||
| INSTANCE_KEY_PUB = load_key(str(env('INSTANCE_KEY_PUB', join(dirname(__file__), 'cert/instance.public.pem')))) | ||||
| TOKEN_EXPIRE_DELTA = relativedelta(hours=1)  # days=1 | ||||
| LEASE_EXPIRE_DELTA = relativedelta(days=int(env('LEASE_EXPIRE_DAYS', 90))) | ||||
| 
 | ||||
| CORS_ORIGINS = env('CORS_ORIGINS').split(',') if (env('CORS_ORIGINS')) else f'https://{DLS_URL}'  # todo: prevent static https | ||||
| CORS_ORIGINS = str(env('CORS_ORIGINS', '')).split(',') if (env('CORS_ORIGINS')) else [f'https://{DLS_URL}'] | ||||
| 
 | ||||
| jwt_encode_key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) | ||||
| jwt_decode_key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) | ||||
| @ -51,29 +50,34 @@ app.add_middleware( | ||||
|     CORSMiddleware, | ||||
|     allow_origins=CORS_ORIGINS, | ||||
|     allow_credentials=True, | ||||
|     allow_methods=["*"], | ||||
|     allow_headers=["*"], | ||||
|     allow_methods=['*'], | ||||
|     allow_headers=['*'], | ||||
| ) | ||||
| 
 | ||||
| logger.setLevel(logging.DEBUG if DEBUG else logging.INFO) | ||||
| 
 | ||||
| 
 | ||||
| def get_token(request: Request) -> dict: | ||||
|     authorization_header = request.headers['authorization'] | ||||
| def __get_token(request: Request) -> dict: | ||||
|     authorization_header = request.headers.get('authorization') | ||||
|     token = authorization_header.split(' ')[1] | ||||
|     return jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False}) | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/', summary='* Index') | ||||
| @app.get('/', summary='Index') | ||||
| async def index(): | ||||
|     return RedirectResponse('/-/readme') | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/status', summary='* Status', description='Returns current service status, version (incl. git-commit) and some variables.', deprecated=True) | ||||
| @app.get('/status', summary='* Status', description='returns current service status, version (incl. git-commit) and some variables.', deprecated=True) | ||||
| async def status(request: Request): | ||||
|     return JSONResponse({'status': 'up', 'version': VERSION, 'commit': COMMIT, 'debug': DEBUG}) | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/', summary='* Index') | ||||
| async def _index(): | ||||
|     return RedirectResponse('/-/readme') | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/health', summary='* Health') | ||||
| async def _health(request: Request): | ||||
|     return JSONResponse({'status': 'up', 'version': VERSION, 'commit': COMMIT, 'debug': DEBUG}) | ||||
| @ -161,8 +165,8 @@ async def _lease_delete(request: Request, lease_ref: str): | ||||
| 
 | ||||
| 
 | ||||
| # venv/lib/python3.9/site-packages/nls_core_service_instance/service_instance_token_manager.py | ||||
| @app.get('/client-token', summary='* Client-Token') | ||||
| async def client_token(): | ||||
| @app.get('/-/client-token', summary='* Client-Token', description='creates a new messenger token for this service instance') | ||||
| async def _client_token(): | ||||
|     cur_time = datetime.utcnow() | ||||
|     exp_time = cur_time + relativedelta(years=12) | ||||
| 
 | ||||
| @ -200,15 +204,20 @@ async def client_token(): | ||||
|     content = jws.sign(payload, key=jwt_encode_key, headers=None, algorithm=ALGORITHMS.RS256) | ||||
| 
 | ||||
|     response = StreamingResponse(iter([content]), media_type="text/plain") | ||||
|     filename = f'client_configuration_token_{datetime.now().strftime("%d-%m-%y-%H-%M-%S")}' | ||||
|     filename = f'client_configuration_token_{datetime.now().strftime("%d-%m-%y-%H-%M-%S")}.tok' | ||||
|     response.headers["Content-Disposition"] = f'attachment; filename={filename}' | ||||
| 
 | ||||
|     return response | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/client-token', summary='* Client-Token', description='creates a new messenger token for this service instance', deprecated=True) | ||||
| async def client_token(): | ||||
|     return RedirectResponse('/-/client-token') | ||||
| 
 | ||||
| 
 | ||||
| # venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py | ||||
| # {"candidate_origin_ref":"00112233-4455-6677-8899-aabbccddeeff","environment":{"fingerprint":{"mac_address_list":["ff:ff:ff:ff:ff:ff"]},"hostname":"my-hostname","ip_address_list":["192.168.178.123","fe80::","fe80::1%enp6s18"],"guest_driver_version":"510.85.02","os_platform":"Debian GNU/Linux 11 (bullseye) 11","os_version":"11 (bullseye)"},"registration_pending":false,"update_pending":false} | ||||
| @app.post('/auth/v1/origin') | ||||
| @app.post('/auth/v1/origin', description='find or create an origin') | ||||
| async def auth_v1_origin(request: Request): | ||||
|     j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() | ||||
| 
 | ||||
| @ -239,7 +248,7 @@ async def auth_v1_origin(request: Request): | ||||
| 
 | ||||
| # venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py | ||||
| # { "environment" : { "guest_driver_version" : "guest_driver_version", "hostname" : "myhost", "ip_address_list" : [ "192.168.1.129" ], "os_version" : "os_version", "os_platform" : "os_platform", "fingerprint" : { "mac_address_list" : [ "e4:b9:7a:e5:7b:ff" ] }, "host_driver_version" : "host_driver_version" }, "origin_ref" : "00112233-4455-6677-8899-aabbccddeeff" } | ||||
| @app.post('/auth/v1/origin/update') | ||||
| @app.post('/auth/v1/origin/update', description='update an origin evidence') | ||||
| async def auth_v1_origin_update(request: Request): | ||||
|     j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() | ||||
| 
 | ||||
| @ -267,7 +276,7 @@ async def auth_v1_origin_update(request: Request): | ||||
| # venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py | ||||
| # venv/lib/python3.9/site-packages/nls_core_auth/auth.py - CodeResponse | ||||
| # {"code_challenge":"...","origin_ref":"00112233-4455-6677-8899-aabbccddeeff"} | ||||
| @app.post('/auth/v1/code') | ||||
| @app.post('/auth/v1/code', description='get an authorization code') | ||||
| async def auth_v1_code(request: Request): | ||||
|     j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() | ||||
| 
 | ||||
| @ -300,7 +309,7 @@ async def auth_v1_code(request: Request): | ||||
| # venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py | ||||
| # venv/lib/python3.9/site-packages/nls_core_auth/auth.py - TokenResponse | ||||
| # {"auth_code":"...","code_verifier":"..."} | ||||
| @app.post('/auth/v1/token') | ||||
| @app.post('/auth/v1/token', description='exchange auth code and verifier for token') | ||||
| async def auth_v1_token(request: Request): | ||||
|     j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() | ||||
|     payload = jwt.decode(token=j['auth_code'], key=jwt_decode_key) | ||||
| @ -337,11 +346,11 @@ async def auth_v1_token(request: Request): | ||||
| 
 | ||||
| 
 | ||||
| # {'fulfillment_context': {'fulfillment_class_ref_list': []}, 'lease_proposal_list': [{'license_type_qualifiers': {'count': 1}, 'product': {'name': 'NVIDIA RTX Virtual Workstation'}}], 'proposal_evaluation_mode': 'ALL_OF', 'scope_ref_list': ['00112233-4455-6677-8899-aabbccddeeff']} | ||||
| @app.post('/leasing/v1/lessor') | ||||
| @app.post('/leasing/v1/lessor', description='request multiple leases (borrow) for current origin') | ||||
| async def leasing_v1_lessor(request: Request): | ||||
|     j, token, cur_time = json.loads((await request.body()).decode('utf-8')), get_token(request), datetime.utcnow() | ||||
|     j, token, cur_time = json.loads((await request.body()).decode('utf-8')), __get_token(request), datetime.utcnow() | ||||
| 
 | ||||
|     origin_ref = token['origin_ref'] | ||||
|     origin_ref = token.get('origin_ref') | ||||
|     scope_ref_list = j['scope_ref_list'] | ||||
|     logging.info(f'> [  create  ]: {origin_ref}: create leases for scope_ref_list {scope_ref_list}') | ||||
| 
 | ||||
| @ -377,11 +386,11 @@ async def leasing_v1_lessor(request: Request): | ||||
| 
 | ||||
| # venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py | ||||
| # 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') | ||||
| @app.get('/leasing/v1/lessor/leases', description='get active leases for current origin') | ||||
| async def leasing_v1_lessor_lease(request: Request): | ||||
|     token, cur_time = get_token(request), datetime.utcnow() | ||||
|     token, cur_time = __get_token(request), datetime.utcnow() | ||||
| 
 | ||||
|     origin_ref = token['origin_ref'] | ||||
|     origin_ref = token.get('origin_ref') | ||||
| 
 | ||||
|     active_lease_list = list(map(lambda x: x.lease_ref, Lease.find_by_origin_ref(db, origin_ref))) | ||||
|     logging.info(f'> [  leases  ]: {origin_ref}: found {len(active_lease_list)} active leases') | ||||
| @ -396,11 +405,11 @@ async def leasing_v1_lessor_lease(request: Request): | ||||
| 
 | ||||
| 
 | ||||
| # venv/lib/python3.9/site-packages/nls_core_lease/lease_single.py | ||||
| @app.put('/leasing/v1/lease/{lease_ref}') | ||||
| @app.put('/leasing/v1/lease/{lease_ref}', description='renew a lease') | ||||
| async def leasing_v1_lease_renew(request: Request, lease_ref: str): | ||||
|     token, cur_time = get_token(request), datetime.utcnow() | ||||
|     token, cur_time = __get_token(request), datetime.utcnow() | ||||
| 
 | ||||
|     origin_ref = token['origin_ref'] | ||||
|     origin_ref = token.get('origin_ref') | ||||
|     logging.info(f'> [  renew   ]: {origin_ref}: renew {lease_ref}') | ||||
| 
 | ||||
|     entity = Lease.find_by_origin_ref_and_lease_ref(db, origin_ref, lease_ref) | ||||
| @ -422,11 +431,36 @@ async def leasing_v1_lease_renew(request: Request, lease_ref: str): | ||||
|     return JSONResponse(response) | ||||
| 
 | ||||
| 
 | ||||
| @app.delete('/leasing/v1/lessor/leases') | ||||
| async def leasing_v1_lessor_lease_remove(request: Request): | ||||
|     token, cur_time = get_token(request), datetime.utcnow() | ||||
| @app.delete('/leasing/v1/lease/{lease_ref}', description='release (return) a lease') | ||||
| async def leasing_v1_lease_delete(request: Request, lease_ref: str): | ||||
|     token, cur_time = __get_token(request), datetime.utcnow() | ||||
| 
 | ||||
|     origin_ref = token['origin_ref'] | ||||
|     origin_ref = token.get('origin_ref') | ||||
|     logging.info(f'> [  return  ]: {origin_ref}: return {lease_ref}') | ||||
| 
 | ||||
|     entity = Lease.find_by_lease_ref(db, lease_ref) | ||||
|     if entity.origin_ref != origin_ref: | ||||
|         raise HTTPException(status_code=403, detail='access or operation forbidden') | ||||
|     if entity is None: | ||||
|         raise HTTPException(status_code=404, detail='requested lease not available') | ||||
| 
 | ||||
|     if Lease.delete(db, lease_ref) == 0: | ||||
|         raise HTTPException(status_code=404, detail='lease not found') | ||||
| 
 | ||||
|     response = { | ||||
|         "lease_ref": lease_ref, | ||||
|         "prompts": None, | ||||
|         "sync_timestamp": cur_time.isoformat(), | ||||
|     } | ||||
| 
 | ||||
|     return JSONResponse(response) | ||||
| 
 | ||||
| 
 | ||||
| @app.delete('/leasing/v1/lessor/leases', description='release all leases') | ||||
| async def leasing_v1_lessor_lease_remove(request: Request): | ||||
|     token, cur_time = __get_token(request), datetime.utcnow() | ||||
| 
 | ||||
|     origin_ref = token.get('origin_ref') | ||||
| 
 | ||||
|     released_lease_list = list(map(lambda x: x.lease_ref, Lease.find_by_origin_ref(db, origin_ref))) | ||||
|     deletions = Lease.cleanup(db, origin_ref) | ||||
|  | ||||
| @ -115,6 +115,13 @@ class Lease(Base): | ||||
|         session.close() | ||||
|         return entities | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def find_by_lease_ref(engine: Engine, lease_ref: str) -> "Lease": | ||||
|         session = sessionmaker(bind=engine)() | ||||
|         entity = session.query(Lease).filter(Lease.lease_ref == lease_ref).first() | ||||
|         session.close() | ||||
|         return entity | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def find_by_origin_ref_and_lease_ref(engine: Engine, origin_ref: str, lease_ref: str) -> "Lease": | ||||
|         session = sessionmaker(bind=engine)() | ||||
| @ -125,7 +132,7 @@ class Lease(Base): | ||||
|     @staticmethod | ||||
|     def renew(engine: Engine, lease: "Lease", lease_expires: datetime.datetime, lease_updated: datetime.datetime): | ||||
|         session = sessionmaker(bind=engine)() | ||||
|         x = dict(lease_expires=lease.lease_expires, lease_updated=lease.lease_updated) | ||||
|         x = dict(lease_expires=lease_expires, lease_updated=lease_updated) | ||||
|         session.execute(update(Lease).where(and_(Lease.origin_ref == lease.origin_ref, Lease.lease_ref == lease.lease_ref)).values(**x)) | ||||
|         session.commit() | ||||
|         session.close() | ||||
|  | ||||
							
								
								
									
										25
									
								
								app/util.py
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								app/util.py
									
									
									
									
									
								
							| @ -1,3 +1,10 @@ | ||||
| def load_file(filename) -> bytes: | ||||
|     with open(filename, 'rb') as file: | ||||
|         content = file.read() | ||||
|     return content | ||||
| 
 | ||||
| 
 | ||||
| def load_key(filename) -> "RsaKey": | ||||
|     try: | ||||
|         # Crypto | Cryptodome on Debian | ||||
|         from Crypto.PublicKey import RSA | ||||
| @ -6,16 +13,16 @@ except ModuleNotFoundError: | ||||
|         from Cryptodome.PublicKey import RSA | ||||
|         from Cryptodome.PublicKey.RSA import RsaKey | ||||
| 
 | ||||
| 
 | ||||
| def load_file(filename) -> bytes: | ||||
|     with open(filename, 'rb') as file: | ||||
|         content = file.read() | ||||
|     return content | ||||
| 
 | ||||
| 
 | ||||
| def load_key(filename) -> RsaKey: | ||||
|     return RSA.import_key(extern_key=load_file(filename), passphrase=None) | ||||
| 
 | ||||
| 
 | ||||
| def generate_key() -> RsaKey: | ||||
| def generate_key() -> "RsaKey": | ||||
|     try: | ||||
|         # Crypto | Cryptodome on Debian | ||||
|         from Crypto.PublicKey import RSA | ||||
|         from Crypto.PublicKey.RSA import RsaKey | ||||
|     except ModuleNotFoundError: | ||||
|         from Cryptodome.PublicKey import RSA | ||||
|         from Cryptodome.PublicKey.RSA import RsaKey | ||||
| 
 | ||||
|     return RSA.generate(bits=2048) | ||||
|  | ||||
							
								
								
									
										38
									
								
								test/main.py
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								test/main.py
									
									
									
									
									
								
							| @ -16,7 +16,7 @@ sys.path.append('../') | ||||
| sys.path.append('../app') | ||||
| 
 | ||||
| from app import main | ||||
| from app.util import generate_key, load_key | ||||
| from app.util import load_key | ||||
| 
 | ||||
| client = TestClient(main.app) | ||||
| 
 | ||||
| @ -33,6 +33,12 @@ jwt_encode_key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), al | ||||
| jwt_decode_key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) | ||||
| 
 | ||||
| 
 | ||||
| def __bearer_token(origin_ref: str) -> str: | ||||
|     token = jwt.encode({"origin_ref": origin_ref}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256) | ||||
|     token = f'Bearer {token}' | ||||
|     return token | ||||
| 
 | ||||
| 
 | ||||
| def test_index(): | ||||
|     response = client.get('/') | ||||
|     assert response.status_code == 200 | ||||
| @ -61,6 +67,11 @@ def test_manage(): | ||||
| 
 | ||||
| 
 | ||||
| def test_client_token(): | ||||
|     response = client.get('/-/client-token') | ||||
|     assert response.status_code == 200 | ||||
| 
 | ||||
| 
 | ||||
| def test_client_token_deprecated(): | ||||
|     response = client.get('/client-token') | ||||
|     assert response.status_code == 200 | ||||
| 
 | ||||
| @ -175,9 +186,7 @@ def test_leasing_v1_lessor(): | ||||
|         'scope_ref_list': [LEASE_REF] | ||||
|     } | ||||
| 
 | ||||
|     bearer_token = jwt.encode({"origin_ref": ORIGIN_REF}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256) | ||||
|     bearer_token = f'Bearer {bearer_token}' | ||||
|     response = client.post('/leasing/v1/lessor', json=payload, headers={'authorization': bearer_token}) | ||||
|     response = client.post('/leasing/v1/lessor', json=payload, headers={'authorization': __bearer_token(ORIGIN_REF)}) | ||||
|     assert response.status_code == 200 | ||||
| 
 | ||||
|     lease_result_list = response.json()['lease_result_list'] | ||||
| @ -186,9 +195,7 @@ def test_leasing_v1_lessor(): | ||||
| 
 | ||||
| 
 | ||||
| def test_leasing_v1_lessor_lease(): | ||||
|     bearer_token = jwt.encode({"origin_ref": ORIGIN_REF}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256) | ||||
|     bearer_token = f'Bearer {bearer_token}' | ||||
|     response = client.get('/leasing/v1/lessor/leases', headers={'authorization': bearer_token}) | ||||
|     response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)}) | ||||
|     assert response.status_code == 200 | ||||
| 
 | ||||
|     active_lease_list = response.json()['active_lease_list'] | ||||
| @ -197,18 +204,23 @@ def test_leasing_v1_lessor_lease(): | ||||
| 
 | ||||
| 
 | ||||
| def test_leasing_v1_lease_renew(): | ||||
|     bearer_token = jwt.encode({"origin_ref": ORIGIN_REF}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256) | ||||
|     bearer_token = f'Bearer {bearer_token}' | ||||
|     response = client.put(f'/leasing/v1/lease/{LEASE_REF}', headers={'authorization': bearer_token}) | ||||
|     response = client.put(f'/leasing/v1/lease/{LEASE_REF}', headers={'authorization': __bearer_token(ORIGIN_REF)}) | ||||
|     assert response.status_code == 200 | ||||
| 
 | ||||
|     assert response.json()['lease_ref'] == LEASE_REF | ||||
| 
 | ||||
| 
 | ||||
| def test_leasing_v1_lease_delete(): | ||||
|     response = client.delete(f'/leasing/v1/lease/{LEASE_REF}', headers={'authorization': __bearer_token(ORIGIN_REF)}) | ||||
|     assert response.status_code == 200 | ||||
| 
 | ||||
|     assert response.json()['lease_ref'] == LEASE_REF | ||||
| 
 | ||||
| 
 | ||||
| def test_leasing_v1_lessor_lease_remove(): | ||||
|     bearer_token = jwt.encode({"origin_ref": ORIGIN_REF}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256) | ||||
|     bearer_token = f'Bearer {bearer_token}' | ||||
|     response = client.delete('/leasing/v1/lessor/leases', headers={'authorization': bearer_token}) | ||||
|     test_leasing_v1_lessor() | ||||
| 
 | ||||
|     response = client.delete('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)}) | ||||
|     assert response.status_code == 200 | ||||
| 
 | ||||
|     released_lease_list = response.json()['released_lease_list'] | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| VERSION=1.1 | ||||
| VERSION=1.2 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Oscar Krause
						Oscar Krause