From 21f6eb030f2ab0bb404cbc480e453c24cb9f7a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Sat, 16 Aug 2025 23:53:09 -0300 Subject: [PATCH] add session route --- http-api/app/routes/enrollments/enroll.py | 4 +- http-api/cli/seeds.py | 2 +- http-api/seeds/test-courses.jsonl | 131 +++++++++--------- id.saladeaula.digital/app/app.py | 4 +- id.saladeaula.digital/app/jose_.py | 52 ------- id.saladeaula.digital/app/routes/authorize.py | 71 ++++++---- id.saladeaula.digital/app/routes/login.py | 91 ------------ id.saladeaula.digital/app/routes/session.py | 107 ++++++++++++++ .../app/templates/__init__.py | 0 .../app/templates/login.html | 115 --------------- .../client/app/routes/home.tsx | 11 +- .../client/app/routes/layout.tsx | 2 +- id.saladeaula.digital/client/wrangler.toml | 11 +- id.saladeaula.digital/template.yaml | 16 +-- .../tests/routes/test_authorize.py | 51 +++++-- .../tests/routes/test_login.py | 49 ------- .../tests/routes/test_session.py | 30 ++++ id.saladeaula.digital/tests/seeds.jsonl | 3 +- id.saladeaula.digital/uv.lock | 98 +------------ layercake/pyproject.toml | 4 +- layercake/uv.lock | 58 +------- 21 files changed, 311 insertions(+), 599 deletions(-) delete mode 100644 id.saladeaula.digital/app/jose_.py delete mode 100644 id.saladeaula.digital/app/routes/login.py create mode 100644 id.saladeaula.digital/app/routes/session.py delete mode 100644 id.saladeaula.digital/app/templates/__init__.py delete mode 100644 id.saladeaula.digital/app/templates/login.html delete mode 100644 id.saladeaula.digital/tests/routes/test_login.py create mode 100644 id.saladeaula.digital/tests/routes/test_session.py diff --git a/http-api/app/routes/enrollments/enroll.py b/http-api/app/routes/enrollments/enroll.py index 37ae73d..bdf48ec 100644 --- a/http-api/app/routes/enrollments/enroll.py +++ b/http-api/app/routes/enrollments/enroll.py @@ -24,8 +24,6 @@ router = Router() enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client) user_layer = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client) -user_collect = DynamoDBCollection(user_layer) -enrollment_collect = DynamoDBCollection(enrollment_layer) processor = BatchProcessor() @@ -54,7 +52,7 @@ class Payload(BaseModel): compress=True, tags=['Enrollment'], middlewares=[ - TenantMiddleware(user_collect), + TenantMiddleware(user_layer.collection), ], ) def enroll_(payload: Payload): diff --git a/http-api/cli/seeds.py b/http-api/cli/seeds.py index a8a0be9..f13cd3b 100644 --- a/http-api/cli/seeds.py +++ b/http-api/cli/seeds.py @@ -15,7 +15,7 @@ JSONL_FILES = ( # 'test-orders.jsonl', 'test-users.jsonl', # 'test-enrollments.jsonl', - # 'test-courses.jsonl', + 'test-courses.jsonl', ) diff --git a/http-api/seeds/test-courses.jsonl b/http-api/seeds/test-courses.jsonl index cbe3de4..5c814e2 100644 --- a/http-api/seeds/test-courses.jsonl +++ b/http-api/seeds/test-courses.jsonl @@ -1,64 +1,67 @@ -{"id": {"S": "439e9a43-ab92-469a-a849-b6e824370f80"}, "access_period": {"S": "360"}, "name": {"S": "No\u00e7\u00f5es em Primeiros Socorros"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:06:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "2c1e724a-58c6-4c20-90df-18b5660d6304"}, "metadata__unit_price": {"N": "199"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "94"}} -{"id": {"S": "15ee05a3-4ceb-4b7e-9979-db75b28c9ade"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem de NR-10 SEP 08 horas"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:02:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "1d86444e-36d6-4ed7-8cea-24a4df9ca15f"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "276"}} -{"id": {"S": "4ea2498a-a6a9-4293-94d0-ceeb248e64b7"}, "access_period": {"S": "720"}, "name": {"S": "NR-10 B\u00e1sico"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:07:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "38"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "21"}} -{"id": {"S": "e1c44881-2fe3-484e-ada2-12b6bf5b9398"}, "access_period": {"S": "720"}, "name": {"S": "NR-35 Seguran\u00e7a nos Trabalhos em Altura (Te\u00f3rico)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:11:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "42"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "1"}} -{"id": {"S": "281198c2-f293-4acc-b96e-e4a2d5f6b73c"}, "access_period": {"S": "360"}, "name": {"S": "CIPA"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:10:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "41"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "3"}} -{"id": {"S": "4866c068-577a-45b0-b41a-41a7dc6b9ab7"}, "access_period": {"S": "360"}, "name": {"S": "Combate a Inc\u00eandio"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:17:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "53"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "18"}} -{"id": {"S": "f10c3283-7722-41c6-ba5d-222f9f4f48af"}, "access_period": {"S": "360"}, "name": {"S": "NR-11 Operador de Empilhadeira"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:13:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "49"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "23"}} -{"id": {"S": "39f89a69-3d94-4dd6-9049-66e540fb2f32"}, "access_period": {"S": "720"}, "name": {"S": "Reciclagem em NR-10 Complementar (SEP)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:20:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "56"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "36"}} -{"id": {"S": "38d8ba20-49a4-4c69-b674-b70a985eb76a"}, "access_period": {"S": "720"}, "name": {"S": "CIPA Grau de Risco 4"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:21:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "56771ce5-4680-4ce4-b257-8f2362975494"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "218"}} -{"id": {"S": "d800d2a9-ae76-46de-be82-3e06ae6afcee"}, "access_period": {"S": "720"}, "name": {"S": "NR-20 B\u00e1sico"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:29:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "70"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "28"}} -{"id": {"S": "3c27ea9c-9464-46a1-9717-8c1441793186"}, "access_period": {"S": "720"}, "name": {"S": "CIPA Grau de Risco 1"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:28:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "6f6fbc20-57f1-4d68-bf00-00119141894f"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "200"}} -{"id": {"S": "96c2553a-d087-42ad-be5e-e960ea673c3d"}, "access_period": {"S": "360"}, "name": {"S": "NR-18 Sinaleiro e Amarrador de Cargas para I\u00e7amento"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:30:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "711bad57-2a03-43ff-91d0-31d151063897"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "270"}} -{"id": {"S": "5c119d4b-573c-4d8d-a99d-63756af2f4c5"}, "access_period": {"S": "360"}, "name": {"S": "NR-06 - Equipamento de Prote\u00e7\u00e3o Individual - EPI"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:32:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "78"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "34"}} -{"id": {"S": "7f7905aa-ec6d-4189-b884-50fa9b1bd0b8"}, "access_period": {"S": "360"}, "name": {"S": "NR-10 Reciclagem: 08 horas"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:01:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "005f262d-9eda-4304-8639-31a86efb3086"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "275"}} -{"id": {"S": "07da69f2-2a2c-4771-b766-633295476ad7"}, "access_period": {"S": "360"}, "name": {"S": "NR-26 Sinaliza\u00e7\u00e3o de Seguran\u00e7a"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:05:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "29b46896-eb93-40ab-8439-25d5129d108a"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "123"}} -{"id": {"S": "d6520884-89e7-4843-b77f-7777c5376d50"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-13 Operador de Caldeiras"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:03:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "1eac1304-6b5a-4fe1-884f-ef2dec753313"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "261"}} -{"id": {"S": "9301601e-385a-4525-a65a-4054f669632f"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-13 Vasos de Press\u00e3o e Unidades de Processo"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:12:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "452f8158-4ed9-4ca5-a53f-c82db430e990"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "262"}} -{"id": {"S": "52b4a909-b6a9-456e-a7b9-c0b3c18ebe00"}, "access_period": {"S": "360"}, "name": {"S": "NR-12 M\u00e1quinas e Equipamentos"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:25:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "62"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "24"}} -{"id": {"S": "b23493dd-6359-4352-97be-12dca3a21ca6"}, "access_period": {"S": "360"}, "name": {"S": "NR-33 Trabalhadores Autorizados e Vigias em Espa\u00e7o Confinado"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:18:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "54"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "32"}} -{"id": {"S": "5c53656d-9557-4ef9-8e05-08d3190bb115"}, "access_period": {"S": "360"}, "name": {"S": "NR-13 Operador de Caldeiras"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:26:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "63"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "25"}} -{"id": {"S": "124ca098-b609-4550-a83c-6b9120a2db42"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-11 Transpaleteiras"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:34:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "7d6e413e-800b-48b6-9b60-f8ac5d3ee730"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "265"}} -{"id": {"S": "6dd8f711-5c5a-477a-971f-122cbac4ce48"}, "access_period": {"S": "720"}, "name": {"S": "NR-35 Supervisor de Trabalho em Altura"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:27:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "64"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "33"}} -{"id": {"S": "4e52d4e9-0566-4f8c-8307-1db770e4c33c"}, "access_period": {"S": "360"}, "name": {"S": "NR-17 Ergonomia"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:31:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "723534ae-36ae-4253-bb73-966c8268779d"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "105"}} -{"id": {"S": "863214e8-26e2-440b-854b-a0ced0164bbf"}, "access_period": {"S": "360"}, "name": {"S": "CIPA Grau de Risco 3 (te\u00f3rico)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:09:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "4079b429-aac4-4a41-937a-38bd6101d875"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "259"}} -{"id": {"S": "7aba7598-83b2-4df7-938c-d075ffef47ca"}, "access_period": {"S": "360"}, "name": {"S": "NR-18 - Constru\u00e7\u00e3o Civil"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:16:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "52"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "27"}} -{"id": {"S": "2e1c93c2-1779-482b-9552-c04e09db8349"}, "access_period": {"S": "720"}, "name": {"S": "NR-10 Complementar (SEP)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:19:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "55"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "22"}} -{"id": {"S": "70827c13-1db5-4499-977f-9a6623e45161"}, "access_period": {"S": "360"}, "name": {"S": "NR-11 Seguran\u00e7a na Opera\u00e7\u00e3o de Rebocadores"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:22:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "56d1c710-36b1-4db5-8a7a-dacb7098dbad"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "154"}} -{"id": {"S": "4a0c4652-fcb3-4362-8fff-bc5ac3ba1cf3"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-11 Plataforma de Trabalho Elevat\u00f3ria (PTA)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:04:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "2706ca6d-2b1b-47aa-8f02-5d39c8833b9d"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "166"}} -{"id": {"S": "c01ec8a2-0359-4351-befb-76c3577339e0"}, "access_period": {"S": "720"}, "name": {"S": "Reciclagem em NR-10 B\u00e1sico"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:08:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "40"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "35"}} -{"id": {"S": "a1a8727c-0519-4692-93e7-81dbe66e167f"}, "access_period": {"S": "360"}, "name": {"S": "Dire\u00e7\u00e3o Defensiva (20 horas)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:15:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "50"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "19"}} -{"id": {"S": "eb19c520-5546-4c57-898d-029c86e59fb6"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-33 Supervisores em Espa\u00e7o Confinado"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:14:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "4ea8aaec-cfd3-4ec1-a15b-a72fac56b371"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "117"}} -{"id": {"S": "386f7086-2871-436f-85f5-31d632fbf624"}, "access_period": {"S": "360"}, "name": {"S": "Boas Pr\u00e1ticas em Manipula\u00e7\u00e3o de Alimentos"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:24:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "59"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "4"}} -{"id": {"S": "3f284753-85ce-4f53-8de7-cdfcdaf9515b"}, "access_period": {"S": "360"}, "name": {"S": "NR-33 Supervisor em Espa\u00e7o Confinado"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:23:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "57"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "31"}} -{"id": {"S": "00ebdd8d-b4db-4437-8814-274811a4c469"}, "access_period": {"S": "360"}, "name": {"S": "Dire\u00e7\u00e3o Defensiva (08 horas)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:33:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "7ac2e34e-232a-427c-a3fc-32198e3a51c6"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "194"}} -{"id": {"S": "8efe00d2-38e2-4281-8f5e-b9113e91374b"}, "access_period": {"S": "1080"}, "name": {"S": "Reciclagem em NR-20 B\u00e1sico"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:43:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "91"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "39"}} -{"id": {"S": "a3c46d94-cf31-4b5f-8de3-6aa1c2d423f0"}, "access_period": {"S": "360"}, "name": {"S": "NR-17 Ergonomia para Teleatendimento/Telemarketing"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:48:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "94dc4c63-9f23-4101-a0d7-d42bfa527cbc"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "269"}} -{"id": {"S": "450a70ca-8ab5-4520-8a22-0e277359797d"}, "access_period": {"S": "365"}, "name": {"S": "NR-18 PEMT PTA"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:52:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "a6775b71-d68a-4263-8ab4-acb3a4f8a8b9"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "184"}} -{"id": {"S": "f05293f0-2ff4-4026-9e65-2f0f67d9f83b"}, "access_period": {"S": "360"}, "name": {"S": "Preven\u00e7\u00e3o e combate ao ass\u00e9dio sexual e \u00e0s demais formas de viol\u00eancia no trabalho"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T01:00:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "da03eac1-e328-49d0-8014-5cdd023cb543"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "260"}} -{"id": {"S": "446426ce-c0f0-4238-83ed-95e8c0434f45"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem de NR-11 Seguran\u00e7a na Opera\u00e7\u00e3o de Rebocadores"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T01:04:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "f78d2241-13fd-45ab-81cc-0acb781b4107"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "189"}} -{"id": {"S": "479516a7-5431-452e-8f28-228e34b86e0c"}, "access_period": {"S": "360"}, "name": {"S": "NR-11 Seguran\u00e7a em Transpaleteira"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T01:03:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "e39a8718-cf10-4dcc-a152-8c50b4b63e14"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "268"}} -{"id": {"S": "95a1fcb9-ba16-4b3c-a59d-047ca32078ff"}, "access_period": {"S": "360"}, "name": {"S": "NR-20 Inicia\u00e7\u00e3o"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:36:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "83"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "29"}} -{"id": {"S": "2b9a6e19-2e2d-4fc2-8924-b45d47057715"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-33 Trabalhos em Espa\u00e7os Confinados"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:42:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "90"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "38"}} -{"id": {"S": "76a5ba94-e11c-48f5-88eb-9326df9be264"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem de NR-12 M\u00e1quinas e Equipamentos"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:45:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "929960de-a62d-4669-91e5-8fef8d670103"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "273"}} -{"id": {"S": "0707270e-623f-486a-8dbd-d852377a208c"}, "access_period": {"S": "720"}, "name": {"S": "Reciclagem em NR-20 - Intermedi\u00e1rio"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:44:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "92"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "57"}} -{"id": {"S": "96c03c32-089c-4ccb-8aa1-73b0f49228b9"}, "access_period": {"S": "360"}, "name": {"S": "Lei Lucas: Primeiros Socorros nas Escolas"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:49:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "9bb3fe7d-e29d-4a2f-91d9-87910152152b"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "278"}} -{"id": {"S": "6a403773-aeac-4e6a-ac39-dc958e4be52a"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-11 - Operador de Empilhadeira"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:47:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "94"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "63"}} -{"id": {"S": "0e39c7af-a812-49aa-ac12-72f4e0ede8c9"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem de NR-18 Plataforma de Trabalho A\u00e9reo PEMT"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:51:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "a52eb5c5-4a5c-404b-96fe-e34037805d1e"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "272"}} -{"id": {"S": "99bb3b60-4ded-4a8e-937c-ba2d78ec6454"}, "access_period": {"S": "720"}, "name": {"S": "CIPA Grau de Risco 2"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:55:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "b4de57c8-59e8-43ad-a6fa-6735d4faa54b"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "206"}} -{"id": {"S": "801d1115-b4e8-4213-96b1-0b4f99bf202e"}, "access_period": {"S": "720"}, "name": {"S": "Reciclagem em NR-18 B\u00e1sico em seguran\u00e7a do trabalho"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:35:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "7e981a6b-8776-4747-bc3b-dcad638b5273"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "266"}} -{"id": {"S": "6689a04a-99c1-4150-b1ed-c131b6dc5bb5"}, "access_period": {"S": "720"}, "name": {"S": "NR-20 Intermedi\u00e1rio"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:37:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "84"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "30"}} -{"id": {"S": "c19cd7ee-3cc8-4f9c-95ff-dad7993f49b1"}, "access_period": {"S": "360"}, "name": {"S": "Gest\u00e3o da Cultura de Seguran\u00e7a"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:40:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "87"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "17"}} -{"id": {"S": "d9f9c3d6-ba97-4695-b1fb-e2539158b064"}, "access_period": {"S": "360"}, "name": {"S": "Reciclagem em NR-20 Avan\u00e7ado II"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:50:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "a3dd23e1-09a0-4c24-a042-08ed65e07fc1"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "131"}} -{"id": {"S": "b945be62-408d-4099-a75c-4d1dda929659"}, "access_period": {"S": "360"}, "name": {"S": "NR-11 Seguran\u00e7a na Opera\u00e7\u00e3o de Pontes Rolantes"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:56:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "bf8b81d6-f83a-4216-bcdb-31ba1e21ffcb"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "148"}} -{"id": {"S": "5c9c1ff1-361f-479d-bd2c-eb3b124a74fc"}, "access_period": {"S": "360"}, "name": {"S": "NR-31 CIPATR"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:54:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "b331f93b-9fc5-4791-bd81-fb10c8f5e401"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "137"}} -{"id": {"S": "80dfc302-4e05-4f23-944d-9a2768cb6c7d"}, "access_period": {"S": "360"}, "name": {"S": "LOTO Lockout e Tagout"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:58:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "d23d569e-51e8-499a-b407-bd782c64a6ac"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "271"}} -{"id": {"S": "c2d1362f-aa7f-40b0-bd15-37570bda5f25"}, "access_period": {"S": "360"}, "name": {"S": "PCA - Programa de Conserva\u00e7\u00e3o Auditiva"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T01:05:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "fa00bc23-d6b8-4dc5-86e7-dc0a9bdb2306"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "247"}} -{"id": {"S": "4682187a-cb5c-47a8-9597-ad9243a6d717"}, "access_period": {"S": "365"}, "name": {"S": "NR-11 Seguran\u00e7a na Opera\u00e7\u00e3o de Talhas"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T01:02:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "e2e25a9a-3104-47a7-90da-0be05a157d21"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "144"}} -{"id": {"S": "a810dd22-56c0-4d9b-8cd2-7e2ee9c45839"}, "access_period": {"N": "360"}, "name": {"S": "NR-11 \u2013 Transporte, movimenta\u00e7\u00e3o, armazenagem e manuseio de materiais"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T01:01:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "dc1a0428-47bf-4db1-a5da-24be49c9fda6"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "100"}, "cert": {"NULL": true}, "update_date": {"S": "2025-04-04T20:30:05.033976-03:00"}} -{"id": {"S": "1c7b1cf0-6973-4271-9407-6e974f0094e9"}, "access_period": {"S": "360"}, "name": {"S": "PPR Programa de Prote\u00e7\u00e3o Respirat\u00f3ria"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:39:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "86d310e8-a87d-4b38-9e43-3dd247c6a522"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "224"}} -{"id": {"S": "6d17d9cf-96be-42a4-bae5-75926e1e832a"}, "access_period": {"S": "720"}, "name": {"S": "Exposi\u00e7\u00e3o ao Benzeno - (Portaria 1109)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:38:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "86"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "20"}} -{"id": {"S": "3b05b03c-8714-4f98-90e0-2a3ac4940035"}, "access_period": {"S": "720"}, "name": {"S": "NR-13 Vasos de Press\u00e3o e Unidades de Processo"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:41:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "89"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "26"}} -{"id": {"S": "30bb357f-2f48-4764-93d1-ffe219cbc5d3"}, "access_period": {"S": "720"}, "name": {"S": "Reciclagem em NR-35 Trabalhos em Altura ( Te\u00f3rico)"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:46:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "93"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "51"}} -{"id": {"S": "f7def039-bf27-496a-94ff-0667c9c0c0db"}, "access_period": {"S": "720"}, "name": {"S": "CIPA Grau de Risco 3"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:53:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "b09ca8da-f342-4a70-aaaa-67ac81569533"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "212"}} -{"id": {"S": "b3d0e345-a8e5-4dc2-a3b0-218bc894ee55"}, "access_period": {"S": "360"}, "name": {"S": "NR-10: No\u00e7\u00f5es do Risco El\u00e9trico"}, "sk": {"S": "0"}, "create_date": {"S": "2024-12-30T00:57:33.088916-03:00"}, "metadata__betaeducacao_id": {"S": "c5b683a3-f432-46e9-94e4-290a788d2ff6"}, "metadata__tenant_id": {"S": "*"}, "metadata__konviva_id": {"S": "274"}} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T01:01:33.088915-03:00", "metadata__konviva_class_id":100, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "a810dd22-56c0-4d9b-8cd2-7e2ee9c45839", "name": "NR-11 Transporte, movimentação, armazenagem e manuseio de materiais", "metadata__unit_price":109} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:01:33.088916-03:00", "metadata__konviva_class_id":275, "tenant_id": "*", "cert": {"exp_interval": 720}, "sk": "0", "id": "7f7905aa-ec6d-4189-b884-50fa9b1bd0b8", "name": "Reciclagem em NR-10 Básico (08 horas)", "metadata__unit_price":169} +{"updated_at": "2025-07-24T17:39:57.432609-03:00", "access_period": 360, "created_at": "2024-12-30T00:56:33.088916-03:00", "metadata__konviva_class_id":148, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "b945be62-408d-4099-a75c-4d1dda929659", "name": "NR-11 Pontes Rolantes"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T01:05:33.088916-03:00", "metadata__konviva_class_id":247, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "c2d1362f-aa7f-40b0-bd15-37570bda5f25", "name": "PCA Programa de Conservação Auditiva", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:12:33.088916-03:00", "metadata__konviva_class_id":262, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "9301601e-385a-4525-a65a-4054f669632f", "name": "Reciclagem em NR-13 Vasos de Pressão e Unidades de Processo", "metadata__unit_price":169} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:58:33.088916-03:00", "metadata__konviva_class_id":271, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "80dfc302-4e05-4f23-944d-9a2768cb6c7d", "name": "LOTO Lockout e Tagout", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:37:33.088916-03:00", "metadata__konviva_class_id":30, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "6689a04a-99c1-4150-b1ed-c131b6dc5bb5", "name": "NR-20 Intermediário", "metadata__unit_price":179} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:03:33.088916-03:00", "metadata__konviva_class_id":261, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "d6520884-89e7-4843-b77f-7777c5376d50", "name": "Reciclagem em NR-13 Operador de Caldeiras", "metadata__unit_price":169} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:50:33.088916-03:00", "metadata__konviva_class_id":131, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "d9f9c3d6-ba97-4695-b1fb-e2539158b064", "name": "Reciclagem em NR-20 Avançado II", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:34:33.088916-03:00", "metadata__konviva_class_id":265, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "124ca098-b609-4550-a83c-6b9120a2db42", "name": "Reciclagem em NR-11 Transpaleteiras", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:27:33.088916-03:00", "metadata__konviva_class_id":33, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "6dd8f711-5c5a-477a-971f-122cbac4ce48", "name": "NR-35 Supervisor de Trabalho em Altura", "metadata__unit_price":169} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 365, "created_at": "2024-12-30T01:02:33.088916-03:00", "metadata__konviva_class_id":144, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "4682187a-cb5c-47a8-9597-ad9243a6d717", "name": "NR-11 Talhas", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:18:33.088916-03:00", "metadata__konviva_class_id":32, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "b23493dd-6359-4352-97be-12dca3a21ca6", "name": "NR-33 Trabalhadores Autorizados e Vigias em Espaço Confinado", "metadata__unit_price":179} +{"updated_at": "2025-07-04T18:09:49.470973+00:00", "access_period": 360, "created_at": "2024-12-30T00:54:33.088916-03:00", "metadata__konviva_class_id":137, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "5c9c1ff1-361f-479d-bd2c-eb3b124a74fc", "name": "NR-31 CIPATR"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2025-07-09T17:31:26.764492-03:00", "metadata__konviva_class_id":148, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "53ef0924-3521-4186-a707-88fbfa137179", "name": "NR-11 Pontes Rolantes", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:31:33.088916-03:00", "metadata__konviva_class_id":105, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "4e52d4e9-0566-4f8c-8307-1db770e4c33c", "name": "NR-17 Ergonomia", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:25:33.088916-03:00", "metadata__konviva_class_id":24, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "52b4a909-b6a9-456e-a7b9-c0b3c18ebe00", "name": "NR-12 Máquinas e Equipamentos", "metadata__unit_price":149} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:40:33.088916-03:00", "metadata__konviva_class_id":17, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "c19cd7ee-3cc8-4f9c-95ff-dad7993f49b1", "name": "Gestão da Cultura de Segurança", "metadata__unit_price":199} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:35:33.088916-03:00", "metadata__konviva_class_id":266, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "801d1115-b4e8-4213-96b1-0b4f99bf202e", "name": "Reciclagem em NR-18 Básico em segurança do trabalho", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:05:33.088916-03:00", "metadata__konviva_class_id":123, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "07da69f2-2a2c-4771-b766-633295476ad7", "name": "NR-26 Sinalização de Segurança", "metadata__unit_price":109} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:26:33.088916-03:00", "metadata__konviva_class_id":25, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "5c53656d-9557-4ef9-8e05-08d3190bb115", "name": "NR-13 Operador de Caldeiras", "metadata__unit_price":199} +{"access_period": 360, "created_at": "2025-07-14T15:09:18.559528-03:00", "metadata__konviva_class_id":281, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "a955518e-ebcb-4441-b914-ddc9ecef84f0", "name": "NR-11 Operador de Munck"} +{"updated_at": "2025-07-24T17:44:10.114431-03:00", "access_period": 360, "created_at": "2024-12-30T00:16:33.088916-03:00", "metadata__konviva_class_id":27, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "7aba7598-83b2-4df7-938c-d075ffef47ca", "name": "NR-18 Construção Civil (descontinuado)"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T01:00:33.088916-03:00", "metadata__konviva_class_id":260, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "f05293f0-2ff4-4026-9e65-2f0f67d9f83b", "name": "Prevenção e combate ao assédio sexual e às demais formas de violência no trabalho", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:48:33.088916-03:00", "metadata__konviva_class_id":269, "tenant_id": "*", "cert": {"exp_interval": 180}, "sk": "0", "id": "a3c46d94-cf31-4b5f-8de3-6aa1c2d423f0", "name": "NR-17 Ergonomia para Teleatendimento/Telemarketing", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T01:04:33.088916-03:00", "metadata__konviva_class_id":189, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "446426ce-c0f0-4238-83ed-95e8c0434f45", "name": "Reciclagem em NR-11 Rebocadores", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:43:33.088916-03:00", "metadata__konviva_class_id":39, "tenant_id": "*", "cert": {"exp_interval": 1000}, "sk": "0", "id": "8efe00d2-38e2-4281-8f5e-b9113e91374b", "name": "Reciclagem em NR-20 Básico", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:22:33.088916-03:00", "metadata__konviva_class_id":154, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "70827c13-1db5-4499-977f-9a6623e45161", "name": "NR-11 Rebocadores", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T01:03:33.088916-03:00", "metadata__konviva_class_id":268, "tenant_id": "*", "cert": {"exp_interval": 365}, "sk": "0", "id": "479516a7-5431-452e-8f28-228e34b86e0c", "name": "NR-11 Transpaleteira", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:19:33.088916-03:00", "metadata__konviva_class_id":22, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "2e1c93c2-1779-482b-9552-c04e09db8349", "name": "NR-10 Complementar (SEP)", "metadata__unit_price":199} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 365, "created_at": "2024-12-30T00:52:33.088916-03:00", "metadata__konviva_class_id":184, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "450a70ca-8ab5-4520-8a22-0e277359797d", "name": "NR-18 PEMT PTA", "metadata__unit_price":149} +{"updated_at": "2025-07-04T18:09:49.470973+00:00", "access_period": 360, "created_at": "2024-12-30T00:09:33.088916-03:00", "metadata__konviva_class_id":259, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "863214e8-26e2-440b-854b-a0ced0164bbf", "name": "CIPA Grau de Risco 3 (teórico)"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:30:33.088916-03:00", "metadata__konviva_class_id":270, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "96c2553a-d087-42ad-be5e-e960ea673c3d", "name": "NR-18 Sinaleiro e Amarrador de Cargas para Içamento", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:46:33.088916-03:00", "metadata__konviva_class_id":51, "tenant_id": "*", "cert": {"exp_interval": 720}, "sk": "0", "id": "30bb357f-2f48-4764-93d1-ffe219cbc5d3", "name": "Reciclagem em NR-35 Trabalhos em Altura", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 720, "created_at": "2024-12-30T00:53:33.088916-03:00", "metadata__konviva_class_id":212, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "f7def039-bf27-496a-94ff-0667c9c0c0db", "name": "CIPA Grau de Risco 3", "metadata__unit_price":109} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:06:33.088916-03:00", "metadata__konviva_class_id":94, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "439e9a43-ab92-469a-a849-b6e824370f80", "name": "Noções em Primeiros Socorros", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:39:33.088916-03:00", "metadata__konviva_class_id":224, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "1c7b1cf0-6973-4271-9407-6e974f0094e9", "name": "PPR Programa de Proteção Respiratória", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:11:33.088916-03:00", "metadata__konviva_class_id":1, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "e1c44881-2fe3-484e-ada2-12b6bf5b9398", "name": "NR-35 Segurança nos Trabalhos em Altura (Teórico)", "metadata__unit_price":119} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:20:33.088916-03:00", "metadata__konviva_class_id":36, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "39f89a69-3d94-4dd6-9049-66e540fb2f32", "name": "Reciclagem em NR-10 Complementar (SEP)", "metadata__unit_price":169} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:32:33.088916-03:00", "metadata__konviva_class_id":34, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "5c119d4b-573c-4d8d-a99d-63756af2f4c5", "name": "NR-06 EPIs Equipamento de Proteção Individual", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:38:33.088916-03:00", "metadata__konviva_class_id":20, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "6d17d9cf-96be-42a4-bae5-75926e1e832a", "name": "NR-20 Exposição ao Benzeno ", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:28:33.088916-03:00", "metadata__konviva_class_id":200, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "3c27ea9c-9464-46a1-9717-8c1441793186", "name": "CIPA Grau de Risco 1", "metadata__unit_price":99} +{"updated_at": "2025-07-24T17:42:24.373883-03:00", "access_period": 360, "created_at": "2024-12-30T00:10:33.088916-03:00", "metadata__konviva_class_id":3, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "281198c2-f293-4acc-b96e-e4a2d5f6b73c", "name": "CIPA (descontinuado)"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:29:33.088916-03:00", "metadata__konviva_class_id":28, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "d800d2a9-ae76-46de-be82-3e06ae6afcee", "name": "NR-20 Básico", "metadata__unit_price":149} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:13:33.088916-03:00", "metadata__konviva_class_id":23, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "f10c3283-7722-41c6-ba5d-222f9f4f48af", "name": "NR-11 Operador de Empilhadeira", "metadata__unit_price":149} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:17:33.088916-03:00", "metadata__konviva_class_id":18, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "4866c068-577a-45b0-b41a-41a7dc6b9ab7", "name": "Combate a Incêndio", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:41:33.088916-03:00", "metadata__konviva_class_id":26, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "3b05b03c-8714-4f98-90e0-2a3ac4940035", "name": "NR-13 Vasos de Pressão e Unidades de Processo", "metadata__unit_price":199} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:21:33.088916-03:00", "metadata__konviva_class_id":218, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "38d8ba20-49a4-4c69-b674-b70a985eb76a", "name": "CIPA Grau de Risco 4", "metadata__unit_price":119} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:02:33.088916-03:00", "metadata__konviva_class_id":276, "tenant_id": "*", "cert": {"exp_interval": 720}, "sk": "0", "id": "15ee05a3-4ceb-4b7e-9979-db75b28c9ade", "name": "Reciclagem em NR-10 SEP (08 horas)", "metadata__unit_price":169} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:07:33.088916-03:00", "metadata__konviva_class_id":21, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "4ea2498a-a6a9-4293-94d0-ceeb248e64b7", "name": "NR-10 Básico", "metadata__unit_price":199} +{"updated_at": "2025-07-24T17:42:14.453946-03:00", "access_period": 360, "created_at": "2024-12-30T00:57:33.088916-03:00", "metadata__konviva_class_id":274, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "b3d0e345-a8e5-4dc2-a3b0-218bc894ee55", "name": "Noções do Risco Elétrico"} +{"updated_at": "2025-07-04T18:09:49.470973+00:00", "access_period": 360, "created_at": "2025-05-12T18:59:12.745375-03:00", "metadata__konviva_class_id":279, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "170884c0-f98e-4b58-8a32-7986f3b41076", "name": "NR-20 Básico - Instalação Classe 1"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:14:33.088916-03:00", "metadata__konviva_class_id":117, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "eb19c520-5546-4c57-898d-029c86e59fb6", "name": "Reciclagem em NR-33 Supervisores em Espaço Confinado", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:42:33.088916-03:00", "metadata__konviva_class_id":38, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "2b9a6e19-2e2d-4fc2-8924-b45d47057715", "name": "Reciclagem em NR-33 Trabalhos em Espaços Confinados", "metadata__unit_price":109} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:49:33.088916-03:00", "metadata__konviva_class_id":278, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "96c03c32-089c-4ccb-8aa1-73b0f49228b9", "name": "Lei Lucas: Primeiros Socorros nas Escolas", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:51:33.088916-03:00", "metadata__konviva_class_id":272, "tenant_id": "*", "cert": {"exp_interval": 720}, "sk": "0", "id": "0e39c7af-a812-49aa-ac12-72f4e0ede8c9", "name": "Reciclagem em NR-18 PEMT Plataforma Elevatória Móvel de Trabalho", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:47:33.088916-03:00", "metadata__konviva_class_id":63, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "6a403773-aeac-4e6a-ac39-dc958e4be52a", "name": "Reciclagem em NR-11 Operador de Empilhadeira", "metadata__unit_price":99} +{"updated_at": "2025-07-04T18:09:49.470973+00:00", "access_period": 360, "created_at": "2024-12-30T00:04:33.088916-03:00", "metadata__konviva_class_id":166, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "4a0c4652-fcb3-4362-8fff-bc5ac3ba1cf3", "name": "Reciclagem em NR-11 Plataforma de Trabalho Elevatória (PTA)"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 720, "created_at": "2024-12-30T00:55:33.088916-03:00", "metadata__konviva_class_id":206, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "99bb3b60-4ded-4a8e-937c-ba2d78ec6454", "name": "CIPA Grau de Risco 2", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:45:33.088916-03:00", "metadata__konviva_class_id":273, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "76a5ba94-e11c-48f5-88eb-9326df9be264", "name": "Reciclagem em NR-12 Máquinas e Equipamentos", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:15:33.088916-03:00", "metadata__konviva_class_id":19, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "a1a8727c-0519-4692-93e7-81dbe66e167f", "name": "Direção Defensiva (20 horas)", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:23:33.088916-03:00", "metadata__konviva_class_id":31, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "3f284753-85ce-4f53-8de7-cdfcdaf9515b", "name": "NR-33 Supervisor em Espaço Confinado", "metadata__unit_price":199} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:36:33.088916-03:00", "metadata__konviva_class_id":29, "tenant_id": "*", "cert": {"exp_interval": 700}, "sk": "0", "id": "95a1fcb9-ba16-4b3c-a59d-047ca32078ff", "name": "NR-20 Iniciação", "metadata__unit_price":99} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:33:33.088916-03:00", "metadata__konviva_class_id":194, "tenant_id": "*", "cert": {"exp_interval": 360}, "sk": "0", "id": "00ebdd8d-b4db-4437-8814-274811a4c469", "name": "Direção Defensiva (08 horas)", "metadata__unit_price":99} +{"updated_at": "2025-07-24T17:42:42.013895-03:00", "access_period": 360, "created_at": "2024-12-30T00:24:33.088916-03:00", "metadata__konviva_class_id":4, "tenant_id": "*", "cert":null, "sk": "0", "id": "386f7086-2871-436f-85f5-31d632fbf624", "name": "Manipulação de Alimentos (descontinuado)"} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 360, "created_at": "2024-12-30T00:08:33.088916-03:00", "metadata__konviva_class_id":35, "tenant_id": "*", "cert": {"exp_interval": 720}, "sk": "0", "id": "c01ec8a2-0359-4351-befb-76c3577339e0", "name": "Reciclagem em NR-10 Básico (20 horas)", "metadata__unit_price":169} +{"updated_at": "2025-08-15T00:00:24.366947-03:00", "access_period": 365, "created_at": "2024-12-30T00:44:33.088916-03:00", "metadata__konviva_class_id":57, "tenant_id": "*", "cert": {"exp_interval": 720}, "sk": "0", "id": "0707270e-623f-486a-8dbd-d852377a208c", "name": "Reciclagem em NR-20 Intermediário", "metadata__unit_price":99} diff --git a/id.saladeaula.digital/app/app.py b/id.saladeaula.digital/app/app.py index 2c1fcc6..f7b3b03 100644 --- a/id.saladeaula.digital/app/app.py +++ b/id.saladeaula.digital/app/app.py @@ -9,15 +9,15 @@ from aws_lambda_powertools.utilities.typing import LambdaContext from routes.authorize import router as authorize from routes.jwks import router as jwks -from routes.login import router as login from routes.openid_configuration import router as openid_configuration +from routes.session import router as session from routes.token import router as token from routes.userinfo import router as userinfo logger = Logger(__name__) tracer = Tracer() app = APIGatewayHttpResolver(enable_validation=True) -app.include_router(login) +app.include_router(session) app.include_router(authorize) app.include_router(jwks) app.include_router(token) diff --git a/id.saladeaula.digital/app/jose_.py b/id.saladeaula.digital/app/jose_.py deleted file mode 100644 index e7d15f9..0000000 --- a/id.saladeaula.digital/app/jose_.py +++ /dev/null @@ -1,52 +0,0 @@ -from datetime import timedelta - -from aws_lambda_powertools.event_handler.exceptions import ForbiddenError -from jose import jwt -from layercake.dateutils import now - -from config import ( - ISSUER, - JWT_ALGORITHM, - JWT_EXP_SECONDS, - JWT_SECRET, - OAUTH2_REFRESH_TOKEN_EXPIRES_IN, -) - - -def generate_jwt(user_id: str, email: str) -> str: - now_ = now() - payload = { - 'sub': user_id, - 'email': email, - 'iat': int(now_.timestamp()), - 'exp': int((now_ + timedelta(seconds=JWT_EXP_SECONDS)).timestamp()), - 'iss': ISSUER, - } - return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM) - - -def generate_refresh_token(user_id: str) -> str: - now_ = now() - exp = now_ + timedelta(seconds=OAUTH2_REFRESH_TOKEN_EXPIRES_IN) - payload = { - 'sub': user_id, - 'iat': int(now_.timestamp()), - 'exp': int(exp.timestamp()), - 'iss': ISSUER, - 'typ': 'refresh', - } - return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM) - - -def verify_jwt(token: str) -> dict: - payload = jwt.decode( - token, - JWT_SECRET, - algorithms=[JWT_ALGORITHM], - issuer=ISSUER, - options={ - 'require': ['exp', 'sub', 'iss'], - 'leeway': 60, - }, - ) - return payload diff --git a/id.saladeaula.digital/app/routes/authorize.py b/id.saladeaula.digital/app/routes/authorize.py index 64dcacd..c690946 100644 --- a/id.saladeaula.digital/app/routes/authorize.py +++ b/id.saladeaula.digital/app/routes/authorize.py @@ -1,49 +1,40 @@ -from http import HTTPStatus from http.cookies import SimpleCookie -from urllib.parse import ParseResult, quote, urlencode, urlunparse +import jwt from authlib.oauth2 import OAuth2Error from authlib.oauth2.rfc6749 import errors from aws_lambda_powertools import Logger -from aws_lambda_powertools.event_handler import Response from aws_lambda_powertools.event_handler.api_gateway import Router -from jose.exceptions import JWTError +from aws_lambda_powertools.event_handler.exceptions import BadRequestError +from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair -from jose_ import verify_jwt +from boto3clients import dynamodb_client +from config import ISSUER, JWT_ALGORITHM, JWT_SECRET, OAUTH2_TABLE from oauth2 import server router = Router() logger = Logger(__name__) +oauth2_layer = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client) @router.get('/authorize') def authorize(): current_event = router.current_event cookies = _parse_cookies(current_event.get('cookies', [])) - id_token = cookies.get('id_token') - continue_url = _build_continue_url( - current_event.path, - current_event.query_string_parameters, - ) + session_id = cookies.get('session_id') - login_url = f'/login?continue={continue_url}' - - try: - if not id_token: - raise ValueError('Missing id_token') - - user = verify_jwt(id_token) - except (ValueError, JWTError): - return Response( - status_code=HTTPStatus.FOUND, - headers={'Location': login_url}, - ) + if not session_id: + raise BadRequestError('Missing session_id') try: + user_id = verify_session(session_id) grant = server.get_consent_grant( request=router.current_event, - end_user={'id': user['sub']}, + end_user={'id': user_id}, ) + except jwt.exceptions.InvalidTokenError as err: + logger.exception(err) + raise BadRequestError(str(err)) except OAuth2Error as err: logger.exception(err) return dict(err.get_body()) @@ -51,7 +42,7 @@ def authorize(): try: return server.create_authorization_response( request=router.current_event, - grant_user={'id': user['sub']}, + grant_user={'id': user_id}, grant=grant, ) except errors.OAuth2Error as err: @@ -59,6 +50,29 @@ def authorize(): return {} +def verify_session(session_id: str) -> str: + payload = jwt.decode( + session_id, + JWT_SECRET, + algorithms=[JWT_ALGORITHM], + issuer=ISSUER, + options={ + 'require': ['exp', 'sub', 'iss', 'sid'], + 'leeway': 60, + }, + ) + + oauth2_layer.collection.get_item( + KeyPair( + pk='SESSION', + sk=payload['sid'], + ), + exc_cls=SessionRevokedError, + ) + + return payload['sub'] + + def _parse_cookies(cookies: list[str] | None) -> dict[str, str]: parsed_cookies = {} @@ -73,9 +87,6 @@ def _parse_cookies(cookies: list[str] | None) -> dict[str, str]: return parsed_cookies -def _build_continue_url( - path: str, - query_string_parameters: dict, -) -> str: - query = urlencode(query_string_parameters) - return quote(urlunparse(ParseResult('', '', path, '', query, '')), safe='') +class SessionRevokedError(BadRequestError): + def __init__(self, *_): + super().__init__('Session revoked') diff --git a/id.saladeaula.digital/app/routes/login.py b/id.saladeaula.digital/app/routes/login.py deleted file mode 100644 index 4bbb3b7..0000000 --- a/id.saladeaula.digital/app/routes/login.py +++ /dev/null @@ -1,91 +0,0 @@ -from http import HTTPStatus -from typing import Annotated - -from aws_lambda_powertools.event_handler import ( - Response, -) -from aws_lambda_powertools.event_handler.api_gateway import Router -from aws_lambda_powertools.event_handler.exceptions import ForbiddenError, NotFoundError -from aws_lambda_powertools.event_handler.openapi.params import Form, Param -from aws_lambda_powertools.shared.cookies import Cookie -from jinja2 import Environment, PackageLoader, select_autoescape -from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair -from passlib.hash import pbkdf2_sha256 - -from boto3clients import dynamodb_client -from config import OAUTH2_TABLE -from jose_ import generate_jwt - -router = Router() -oauth2_layer = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client) -templates = Environment( - loader=PackageLoader('app'), - autoescape=select_autoescape(['html']), -) - - -@router.get('/login', compress=True) -def login_form(continue_: Annotated[str, Param(alias='continue')]): - template = templates.get_template('login.html') - html = template.render(**{'continue': continue_}) - - return Response( - body=html, - status_code=HTTPStatus.OK, - content_type='text/html', - ) - - -@router.post('/login') -def login( - username: Annotated[str, Form()], - password: Annotated[str, Form()], - continue_: Annotated[str, Form(alias='continue')], -): - user_id, password_hash = _get_user(username) - - if not pbkdf2_sha256.verify(password, password_hash): - raise ForbiddenError('Invalid credentials') - - jwt_token = generate_jwt(user_id, username) - - return Response( - status_code=HTTPStatus.FOUND, - headers={ - 'Location': continue_, - }, - cookies=[ - Cookie( - name='id_token', - value=jwt_token, - http_only=True, - same_site=None, - ), - ], - ) - - -def _get_user(username: str) -> tuple[str, str]: - r = oauth2_layer.collection.get_item( - # Post-migration: uncomment the following line - # KeyPair('EMAIL', username), - KeyPair('email', username), - exc_cls=EmailNotFoundError, - ) - - password = oauth2_layer.collection.get_item( - KeyPair(r['user_id'], 'PASSWORD'), - exc_cls=UserNotFoundError, - ) - - return r['user_id'], password['hash'] - - -class EmailNotFoundError(NotFoundError): - def __init__(self, *_): - super().__init__('Email not found') - - -class UserNotFoundError(NotFoundError): - def __init__(self, *_): - super().__init__('User not found') diff --git a/id.saladeaula.digital/app/routes/session.py b/id.saladeaula.digital/app/routes/session.py new file mode 100644 index 0000000..4d0626c --- /dev/null +++ b/id.saladeaula.digital/app/routes/session.py @@ -0,0 +1,107 @@ +from http import HTTPStatus +from typing import Annotated +from uuid import uuid4 + +import jwt +from aws_lambda_powertools.event_handler import ( + Response, +) +from aws_lambda_powertools.event_handler.api_gateway import Router +from aws_lambda_powertools.event_handler.exceptions import ForbiddenError, NotFoundError +from aws_lambda_powertools.event_handler.openapi.params import Body +from aws_lambda_powertools.shared.cookies import Cookie +from layercake.dateutils import now, ttl +from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey +from passlib.hash import pbkdf2_sha256 + +from boto3clients import dynamodb_client +from config import ISSUER, JWT_ALGORITHM, JWT_EXP_SECONDS, JWT_SECRET, OAUTH2_TABLE + +router = Router() +oauth2_layer = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client) + + +@router.post('/session') +def session( + username: Annotated[str, Body()], + password: Annotated[str, Body()], +): + user_id, password_hash = _get_user(username) + + if not pbkdf2_sha256.verify(password, password_hash): + raise ForbiddenError('Invalid credentials') + + return Response( + status_code=HTTPStatus.FOUND, + cookies=[ + Cookie( + name='session_id', + value=new_session(user_id), + http_only=True, + secure=True, + same_site=None, + ) + ], + ) + + +def _get_user(username: str) -> tuple[str, str]: + sk = SortKey(username, path_spec='user_id') + user = oauth2_layer.collection.get_items( + KeyPair(pk='email', sk=sk, rename_key=sk.path_spec) + + KeyPair(pk='cpf', sk=sk, rename_key=sk.path_spec), + flatten_top=False, + ) + + if not user: + raise UserNotFoundError() + + password = oauth2_layer.collection.get_item( + KeyPair(user['user_id'], 'PASSWORD'), + exc_cls=UserNotFoundError, + ) + + return user['user_id'], password['hash'] + + +def new_session(sub: str) -> str: + now_ = now() + sid = str(uuid4()) + exp = ttl(start_dt=now_, seconds=JWT_EXP_SECONDS) + token = jwt.encode( + { + 'sid': sid, + 'sub': sub, + 'iss': ISSUER, + 'iat': int(now_.timestamp()), + 'exp': exp, + }, + JWT_SECRET, + algorithm=JWT_ALGORITHM, + ) + + with oauth2_layer.transact_writer() as transact: + transact.put( + item={ + 'id': 'SESSION', + 'sk': sid, + 'user_id': sub, + 'ttl': exp, + 'created_at': now_, + } + ) + transact.put( + item={ + 'id': sub, + 'sk': f'SESSION#{sid}', + 'ttl': exp, + 'created_at': now_, + } + ) + + return token + + +class UserNotFoundError(NotFoundError): + def __init__(self, *_): + super().__init__('User not found') diff --git a/id.saladeaula.digital/app/templates/__init__.py b/id.saladeaula.digital/app/templates/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/id.saladeaula.digital/app/templates/login.html b/id.saladeaula.digital/app/templates/login.html deleted file mode 100644 index 5baff2e..0000000 --- a/id.saladeaula.digital/app/templates/login.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - EDUSEG® - - - - - -
- - -
-
- - - - - - -

- Faça login -

-

- Não tem uma conta? - Cadastre-se. -

-
- -
- -
- - -
- -
-
- - Esqueceu sua senha? -
- -
- - -
- -

- Ao fazer login, você concorda com nossa - - política de privacidade . -

-
-
- - diff --git a/id.saladeaula.digital/client/app/routes/home.tsx b/id.saladeaula.digital/client/app/routes/home.tsx index dc1fb8e..8f26125 100644 --- a/id.saladeaula.digital/client/app/routes/home.tsx +++ b/id.saladeaula.digital/client/app/routes/home.tsx @@ -22,14 +22,11 @@ const schema = z.object({ type Schema = z.infer export function meta({}: Route.MetaArgs) { - return [ - { title: 'EDUSEG®' }, - { name: 'description', content: 'Welcome to React Router!' } - ] + return [{ title: 'EDUSEG®' }] } export function loader({ context }: Route.LoaderArgs) { - return { message: context.cloudflare.env.VALUE_FROM_CLOUDFLARE } + return { message: context.cloudflare.env.ISSUER_URL } } export default function Home({ loaderData }: Route.ComponentProps) { @@ -67,7 +64,7 @@ export default function Home({ loaderData }: Route.ComponentProps) {
- +
@@ -80,7 +77,7 @@ export default function Home({ loaderData }: Route.ComponentProps) { Esqueceu sua senha?
- +