tina4-python 0.1.52__tar.gz → 0.2.64__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {tina4_python-0.1.52 → tina4_python-0.2.64}/PKG-INFO +182 -26
  2. {tina4_python-0.1.52 → tina4_python-0.2.64}/README.md +176 -23
  3. {tina4_python-0.1.52 → tina4_python-0.2.64}/pyproject.toml +8 -2
  4. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Auth.py +44 -6
  5. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Constant.py +5 -1
  6. tina4_python-0.2.64/tina4_python/Database.py +573 -0
  7. tina4_python-0.2.64/tina4_python/DatabaseResult.py +101 -0
  8. tina4_python-0.2.64/tina4_python/Debug.py +91 -0
  9. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Env.py +10 -5
  10. tina4_python-0.2.64/tina4_python/MiddleWare.py +75 -0
  11. tina4_python-0.2.64/tina4_python/Migration.py +99 -0
  12. tina4_python-0.2.64/tina4_python/ORM.py +519 -0
  13. tina4_python-0.2.64/tina4_python/Queue.py +495 -0
  14. tina4_python-0.2.64/tina4_python/Request.py +18 -0
  15. tina4_python-0.2.64/tina4_python/Response.py +98 -0
  16. tina4_python-0.2.64/tina4_python/Router.py +385 -0
  17. tina4_python-0.2.64/tina4_python/Session.py +252 -0
  18. tina4_python-0.2.64/tina4_python/ShellColors.py +20 -0
  19. tina4_python-0.2.64/tina4_python/Swagger.py +228 -0
  20. tina4_python-0.2.64/tina4_python/Template.py +91 -0
  21. tina4_python-0.2.64/tina4_python/Webserver.py +385 -0
  22. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/__init__.py +128 -37
  23. tina4_python-0.2.64/tina4_python/public/favicon.ico +0 -0
  24. tina4_python-0.2.64/tina4_python/public/images/403.png +0 -0
  25. tina4_python-0.2.64/tina4_python/public/images/404.png +0 -0
  26. tina4_python-0.2.64/tina4_python/public/images/500.png +0 -0
  27. tina4_python-0.2.64/tina4_python/public/images/logo.png +0 -0
  28. tina4_python-0.2.64/tina4_python/public/js/tina4helper.js +397 -0
  29. tina4_python-0.2.64/tina4_python/public/swagger/index.html +90 -0
  30. tina4_python-0.2.64/tina4_python/public/swagger/oauth2-redirect.html +63 -0
  31. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/templates/errors/403.twig +1 -2
  32. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/templates/errors/404.twig +1 -2
  33. tina4_python-0.2.64/tina4_python/templates/errors/500.twig +11 -0
  34. tina4_python-0.1.52/tina4_python/Debug.py +0 -33
  35. tina4_python-0.1.52/tina4_python/Request.py +0 -21
  36. tina4_python-0.1.52/tina4_python/Response.py +0 -40
  37. tina4_python-0.1.52/tina4_python/Router.py +0 -208
  38. tina4_python-0.1.52/tina4_python/Session.py +0 -115
  39. tina4_python-0.1.52/tina4_python/Template.py +0 -47
  40. tina4_python-0.1.52/tina4_python/Webserver.py +0 -225
  41. tina4_python-0.1.52/tina4_python/public/favicon.ico +0 -0
  42. tina4_python-0.1.52/tina4_python/public/images/403.png +0 -0
  43. tina4_python-0.1.52/tina4_python/public/images/404.png +0 -0
  44. tina4_python-0.1.52/tina4_python/public/images/logo.png +0 -0
  45. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Localization.py +0 -0
  46. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/Messages.py +0 -0
  47. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/messages.pot +0 -0
  48. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/public/css/readme.md +0 -0
  49. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/public/images/readme.md +0 -0
  50. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/public/js/readme.md +0 -0
  51. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/templates/readme.md +0 -0
  52. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  53. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  54. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  55. {tina4_python-0.1.52 → tina4_python-0.2.64}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: tina4-python
3
- Version: 0.1.52
3
+ Version: 0.2.64
4
4
  Summary: Tina4Python - This is not another framework for Python
5
5
  Author: Andre van Zuydam
6
6
  Author-email: andrevanzuydam@gmail.com
@@ -9,9 +9,12 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
- Requires-Dist: Jinja2 (>=3.0.3,<4.0.0)
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: Jinja2 (>=3.1.4,<4.0.0)
14
+ Requires-Dist: bcrypt (>=4.1.3,<5.0.0)
13
15
  Requires-Dist: cryptography (>=42.0.5,<43.0.0)
14
16
  Requires-Dist: libsass (>=0.22.0,<0.23.0)
17
+ Requires-Dist: litequeue (>=0.9,<0.10)
15
18
  Requires-Dist: mypy (>=0.991,<0.992)
16
19
  Requires-Dist: pyjwt (>=2.8.0,<3.0.0)
17
20
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
@@ -56,6 +59,22 @@ If you are developing on Tina4, make sure you copy the public folder from tina4_
56
59
 
57
60
  ### Installation
58
61
 
62
+ Virtual environment
63
+
64
+ Linux / Mac
65
+ ```bash
66
+ python3 -m venv venv
67
+ source ./venv/bin/activate
68
+ pip install poetry
69
+ ```
70
+
71
+ Windows
72
+ ```bash
73
+ python -m venv venv
74
+ .\venv\Scripts\activate
75
+ pip install poetry
76
+ ```
77
+
59
78
  #### Windows
60
79
 
61
80
  1.) Install Poetry:
@@ -158,10 +177,10 @@ from tina4_python.Router import get
158
177
  from tina4_python.Response import Response
159
178
 
160
179
  @get("/hello/{name}")
161
- async def greet(**params):
180
+ async def greet(**params): #(request, response)
162
181
  name = params['request'].params['name']
163
- return params['response'](f"Hello, {name}!")
164
- ````
182
+ return params['response'](f"Hello, {name}!") # return response()
183
+ ````
165
184
 
166
185
  This code creates a route for a GET request to `/hello/{name}`, where `name` is a parameter in the URL. The function `greet` accepts this parameter and responds with a personalized greeting.
167
186
 
@@ -201,6 +220,16 @@ async def capture_get(request, response):
201
220
  @post("/capture")
202
221
  async def capture_post(request, response):
203
222
  return response(request.body)
223
+
224
+ @get("/simple")
225
+ async def simple_get(request, response):
226
+ print("<h1>Hello World!</h1>")
227
+
228
+ @get("/redirect")
229
+ async def simple_get(request, response):
230
+
231
+ return response.redirect("/test")
232
+
204
233
  ```
205
234
 
206
235
  In your ```src/__init__.py``` add the following code
@@ -224,33 +253,150 @@ For ease of use you can supply an `API_KEY` param to your .env with a secret of
224
253
  API_KEY=somehash
225
254
  ```
226
255
 
256
+ ### Suppressing the default webservice
257
+
258
+ You may want to use your Tina4 project as a library in another project so suppressing the default webservice is probably needed.
259
+
260
+ ```.env
261
+ TINA4_DEFAULT_WEBSERVER="False"
262
+ ```
263
+ or in your `__init__.py` file
264
+ ```python
265
+ import os
266
+ os.environ["TINA4_DEFAULT_WEBSERVER"] = "False"
267
+ ```
268
+ Additionally, it will require you to rename `src` to the name of your package
227
269
 
228
270
  ### Features
229
- | Completed | To Do |
230
- |----------------------------|-------------------|
231
- | Python pip package | |
232
- | Basic environment handling | |
233
- | Basic routing | OpenAPI (Swagger) |
234
- | Enhanced routing | |
235
- | CSS Support | |
236
- | Image Support | |
237
- | Localization | |
238
- | Error Pages | |
239
- | Template handling | |
240
- | Form posting | |
241
- | JWT tokens & security | |
271
+ | Completed | To Do |
272
+ |----------------------------|-------|
273
+ | Python pip package | |
274
+ | Basic environment handling | |
275
+ | Routing with Swagger | |
276
+ | Enhanced Routing | |
277
+ | CSS Support | |
278
+ | Image Support | |
279
+ | Localization | |
280
+ | Error Pages | |
281
+ | Template handling | |
282
+ | Form posting | |
283
+ | Migrations | |
284
+ | Colored Debugging | |
285
+ | Database Abstraction | |
286
+
287
+ ### Database
288
+
289
+ ```bash
290
+
291
+ Various databases initialised:
292
+
293
+ dba = Database("sqlite3:test.db", "username", "password")
294
+ dba = Database("mysql:localhost/3306:myschema", "username", "password")
295
+ dba = Database("postgres:localhost/5432:myschema", "username", "password")
296
+ dba = Database("firebird:localhost/3050:/home/database/FIREBIRD.FDB", "username", "password")
297
+
298
+ NoSQL support (Still to be developed):
299
+
300
+ dba = Database("mongodb:localhost/27017:mycollection", "username", "password")
301
+ dba = Database("firebase:https://your_storage.firebaseio.com", "username", "password")
302
+
303
+ Fetching records and passing data as parameters, limit and skip specified:
304
+
305
+ records = dba.fetch("select * from table where something = ? and something2 = ?", params=["something", "something2"], limit=10, skip=5)
306
+
307
+ print (records)
308
+ print (records.to_json())
309
+ {
310
+ id : 1
311
+ something: "something",
312
+ something2: "something2"
313
+ }
314
+
315
+ print(records[0].id)
316
+ 1
317
+
318
+ record = dba.fetch_one("select * from table where something = ? and something2 = ?", params=["something", "something2"])
319
+
320
+ print(record.id)
321
+
322
+ print (records.to_json())
323
+ 1
324
+
325
+
326
+ Executing sql queries:
327
+
328
+ dba.execute ("update table set something = ? and something2 = ? where id = ?", params=["something", "something2", 1])
329
+ dba.execute ("delete from table where id = ?", params=[1])
330
+
331
+
332
+ Starting a transaction:
333
+
334
+ dba.start_transaction()
335
+
336
+
337
+ Rollback a transaction:
338
+
339
+ dba.roll_back()
340
+
341
+
342
+ Commit a transaction:
343
+
344
+ dba.commit()
345
+
346
+
347
+ Select method (Still in development):
348
+
349
+ dba.select(["id", "something", "something2"], "table_name", filter={"id": 1}, limit=10, skip=0)
350
+ dba.select(["id", "something", "something2"], "table_name", filter=[{"id": 1}, {"id": 2}], limit=10, skip=0)
351
+ dba.select(["id", "something", "sum(id)"])
352
+ .from(["table"])
353
+ .join(["tabel2"])
354
+ .on([""])
355
+ .and([{"id": 1}])
356
+ .where({"id" : 2}, "id = ?", [{"id": 2}])
357
+ .having()
358
+ .group_by()
359
+ .order_by(["id"])
360
+
361
+
362
+ Updating records - records will be found by primary key specified and then updated:
363
+
364
+ dba.update(table_name="table_name", records.fromJSON(json_string))
365
+ dba.update(table_name="table_name", records, primary_key="id")
366
+ dba.update(table_name="table_name", record, primary_key="id") # primary key implied by first key value pair
367
+
368
+
369
+ Inserting records - pass a dictionary for a single record and a list of dictionaries for multiple records:
370
+
371
+ dba.insert(table_name="table_name", dba.from_json(json_string))
372
+ dba.insert(table_name="table_name", {"id": 1, "something": "hello", "something2": "world"})
373
+ dba.insert(table_name="table_name", [{"id": 1, "something": "hello", "something2": "world"},
374
+ {"id": 2, "something": "hello2", "something2": "world2"}])
375
+
376
+
377
+ Deleting records - specify table name. Records can either be found by primary key or filter:
378
+
379
+ dba.delete("table_name", record, primary_key="id")
380
+ dba.delete("table_name", filter={"id": 1})
381
+ dba.delete("table_name", filter=[{"id": 1}, {"id": 2}])
382
+
383
+ Migration updates:
384
+
385
+ - record count added
386
+ - Database result object updated
387
+ ```
242
388
 
243
389
  ### Building and Deployment
244
390
 
245
391
  #### Using Python
246
392
 
247
- 1. Building the package:
248
- ```bash
249
- python3 -m pip install --upgrade build
250
- python3 -m build
251
- python3 -m pip install --upgrade twine
252
- python3 -m twine upload dist/*
253
- ```
393
+ Building the package without poetry - you will need to request an API key:
394
+ ```bash
395
+ python3 -m pip install --upgrade build
396
+ python3 -m build
397
+ python3 -m pip install --upgrade twine
398
+ python3 -m twine upload dist/*
399
+ ```
254
400
 
255
401
  #### Using Poetry
256
402
 
@@ -276,3 +422,13 @@ Flake8 Code tests
276
422
  poetry run flake8 ./tina4_python
277
423
  ```
278
424
 
425
+ ### Using queues
426
+
427
+ ```bash
428
+ docker run -d --hostname=my-kafka --name=some-kafka -p 9092:9092 apache/kafka
429
+ ```
430
+
431
+ ```bash
432
+ docker run -d --hostname=my-rabbit --name=some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3
433
+ ```
434
+
@@ -36,6 +36,22 @@ If you are developing on Tina4, make sure you copy the public folder from tina4_
36
36
 
37
37
  ### Installation
38
38
 
39
+ Virtual environment
40
+
41
+ Linux / Mac
42
+ ```bash
43
+ python3 -m venv venv
44
+ source ./venv/bin/activate
45
+ pip install poetry
46
+ ```
47
+
48
+ Windows
49
+ ```bash
50
+ python -m venv venv
51
+ .\venv\Scripts\activate
52
+ pip install poetry
53
+ ```
54
+
39
55
  #### Windows
40
56
 
41
57
  1.) Install Poetry:
@@ -138,10 +154,10 @@ from tina4_python.Router import get
138
154
  from tina4_python.Response import Response
139
155
 
140
156
  @get("/hello/{name}")
141
- async def greet(**params):
157
+ async def greet(**params): #(request, response)
142
158
  name = params['request'].params['name']
143
- return params['response'](f"Hello, {name}!")
144
- ````
159
+ return params['response'](f"Hello, {name}!") # return response()
160
+ ````
145
161
 
146
162
  This code creates a route for a GET request to `/hello/{name}`, where `name` is a parameter in the URL. The function `greet` accepts this parameter and responds with a personalized greeting.
147
163
 
@@ -181,6 +197,16 @@ async def capture_get(request, response):
181
197
  @post("/capture")
182
198
  async def capture_post(request, response):
183
199
  return response(request.body)
200
+
201
+ @get("/simple")
202
+ async def simple_get(request, response):
203
+ print("<h1>Hello World!</h1>")
204
+
205
+ @get("/redirect")
206
+ async def simple_get(request, response):
207
+
208
+ return response.redirect("/test")
209
+
184
210
  ```
185
211
 
186
212
  In your ```src/__init__.py``` add the following code
@@ -204,33 +230,150 @@ For ease of use you can supply an `API_KEY` param to your .env with a secret of
204
230
  API_KEY=somehash
205
231
  ```
206
232
 
233
+ ### Suppressing the default webservice
234
+
235
+ You may want to use your Tina4 project as a library in another project so suppressing the default webservice is probably needed.
236
+
237
+ ```.env
238
+ TINA4_DEFAULT_WEBSERVER="False"
239
+ ```
240
+ or in your `__init__.py` file
241
+ ```python
242
+ import os
243
+ os.environ["TINA4_DEFAULT_WEBSERVER"] = "False"
244
+ ```
245
+ Additionally, it will require you to rename `src` to the name of your package
207
246
 
208
247
  ### Features
209
- | Completed | To Do |
210
- |----------------------------|-------------------|
211
- | Python pip package | |
212
- | Basic environment handling | |
213
- | Basic routing | OpenAPI (Swagger) |
214
- | Enhanced routing | |
215
- | CSS Support | |
216
- | Image Support | |
217
- | Localization | |
218
- | Error Pages | |
219
- | Template handling | |
220
- | Form posting | |
221
- | JWT tokens & security | |
248
+ | Completed | To Do |
249
+ |----------------------------|-------|
250
+ | Python pip package | |
251
+ | Basic environment handling | |
252
+ | Routing with Swagger | |
253
+ | Enhanced Routing | |
254
+ | CSS Support | |
255
+ | Image Support | |
256
+ | Localization | |
257
+ | Error Pages | |
258
+ | Template handling | |
259
+ | Form posting | |
260
+ | Migrations | |
261
+ | Colored Debugging | |
262
+ | Database Abstraction | |
263
+
264
+ ### Database
265
+
266
+ ```bash
267
+
268
+ Various databases initialised:
269
+
270
+ dba = Database("sqlite3:test.db", "username", "password")
271
+ dba = Database("mysql:localhost/3306:myschema", "username", "password")
272
+ dba = Database("postgres:localhost/5432:myschema", "username", "password")
273
+ dba = Database("firebird:localhost/3050:/home/database/FIREBIRD.FDB", "username", "password")
274
+
275
+ NoSQL support (Still to be developed):
276
+
277
+ dba = Database("mongodb:localhost/27017:mycollection", "username", "password")
278
+ dba = Database("firebase:https://your_storage.firebaseio.com", "username", "password")
279
+
280
+ Fetching records and passing data as parameters, limit and skip specified:
281
+
282
+ records = dba.fetch("select * from table where something = ? and something2 = ?", params=["something", "something2"], limit=10, skip=5)
283
+
284
+ print (records)
285
+ print (records.to_json())
286
+ {
287
+ id : 1
288
+ something: "something",
289
+ something2: "something2"
290
+ }
291
+
292
+ print(records[0].id)
293
+ 1
294
+
295
+ record = dba.fetch_one("select * from table where something = ? and something2 = ?", params=["something", "something2"])
296
+
297
+ print(record.id)
298
+
299
+ print (records.to_json())
300
+ 1
301
+
302
+
303
+ Executing sql queries:
304
+
305
+ dba.execute ("update table set something = ? and something2 = ? where id = ?", params=["something", "something2", 1])
306
+ dba.execute ("delete from table where id = ?", params=[1])
307
+
308
+
309
+ Starting a transaction:
310
+
311
+ dba.start_transaction()
312
+
313
+
314
+ Rollback a transaction:
315
+
316
+ dba.roll_back()
317
+
318
+
319
+ Commit a transaction:
320
+
321
+ dba.commit()
322
+
323
+
324
+ Select method (Still in development):
325
+
326
+ dba.select(["id", "something", "something2"], "table_name", filter={"id": 1}, limit=10, skip=0)
327
+ dba.select(["id", "something", "something2"], "table_name", filter=[{"id": 1}, {"id": 2}], limit=10, skip=0)
328
+ dba.select(["id", "something", "sum(id)"])
329
+ .from(["table"])
330
+ .join(["tabel2"])
331
+ .on([""])
332
+ .and([{"id": 1}])
333
+ .where({"id" : 2}, "id = ?", [{"id": 2}])
334
+ .having()
335
+ .group_by()
336
+ .order_by(["id"])
337
+
338
+
339
+ Updating records - records will be found by primary key specified and then updated:
340
+
341
+ dba.update(table_name="table_name", records.fromJSON(json_string))
342
+ dba.update(table_name="table_name", records, primary_key="id")
343
+ dba.update(table_name="table_name", record, primary_key="id") # primary key implied by first key value pair
344
+
345
+
346
+ Inserting records - pass a dictionary for a single record and a list of dictionaries for multiple records:
347
+
348
+ dba.insert(table_name="table_name", dba.from_json(json_string))
349
+ dba.insert(table_name="table_name", {"id": 1, "something": "hello", "something2": "world"})
350
+ dba.insert(table_name="table_name", [{"id": 1, "something": "hello", "something2": "world"},
351
+ {"id": 2, "something": "hello2", "something2": "world2"}])
352
+
353
+
354
+ Deleting records - specify table name. Records can either be found by primary key or filter:
355
+
356
+ dba.delete("table_name", record, primary_key="id")
357
+ dba.delete("table_name", filter={"id": 1})
358
+ dba.delete("table_name", filter=[{"id": 1}, {"id": 2}])
359
+
360
+ Migration updates:
361
+
362
+ - record count added
363
+ - Database result object updated
364
+ ```
222
365
 
223
366
  ### Building and Deployment
224
367
 
225
368
  #### Using Python
226
369
 
227
- 1. Building the package:
228
- ```bash
229
- python3 -m pip install --upgrade build
230
- python3 -m build
231
- python3 -m pip install --upgrade twine
232
- python3 -m twine upload dist/*
233
- ```
370
+ Building the package without poetry - you will need to request an API key:
371
+ ```bash
372
+ python3 -m pip install --upgrade build
373
+ python3 -m build
374
+ python3 -m pip install --upgrade twine
375
+ python3 -m twine upload dist/*
376
+ ```
234
377
 
235
378
  #### Using Poetry
236
379
 
@@ -255,3 +398,13 @@ Flake8 Code tests
255
398
  ```
256
399
  poetry run flake8 ./tina4_python
257
400
  ```
401
+
402
+ ### Using queues
403
+
404
+ ```bash
405
+ docker run -d --hostname=my-kafka --name=some-kafka -p 9092:9092 apache/kafka
406
+ ```
407
+
408
+ ```bash
409
+ docker run -d --hostname=my-rabbit --name=some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3
410
+ ```
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "tina4-python"
3
- version = "0.1.52"
3
+ version = "0.2.64"
4
4
  description = "Tina4Python - This is not another framework for Python"
5
5
  authors = ["Andre van Zuydam <andrevanzuydam@gmail.com>"]
6
6
  readme = "README.md"
@@ -9,18 +9,24 @@ readme = "README.md"
9
9
 
10
10
  [tool.poetry.dependencies]
11
11
  python = "^3.10"
12
- Jinja2 = "^3.0.3"
12
+ Jinja2 = "^3.1.4"
13
13
  mypy = "^0.991"
14
14
  libsass = "^0.22.0"
15
15
  python-dotenv = "^1.0.1"
16
16
  pyjwt = "^2.8.0"
17
17
  cryptography = "^42.0.5"
18
18
  watchdog = "^4.0.0"
19
+ bcrypt = "^4.1.3"
20
+ litequeue = "^0.9"
19
21
 
20
22
  [tool.poetry.group.dev.dependencies]
21
23
  flake8 = "^7.0.0"
22
24
  pytest = "^7.2.0"
23
25
  jurigged = "^0.5.3"
26
+ safety = "^3.2.8"
27
+ ruff = "^0.7.0"
28
+ mysql-connector-python = "^9.1.0"
29
+ psycopg2-binary = "^2.9.10"
24
30
 
25
31
  [build-system]
26
32
  requires = ["poetry-core>=1.0.0"]
@@ -6,8 +6,9 @@
6
6
  # flake8: noqa: E501
7
7
  import datetime
8
8
  import os
9
-
10
9
  import jwt
10
+ import bcrypt
11
+ from json import JSONEncoder
11
12
  from cryptography import x509
12
13
  from cryptography.x509 import NameOID
13
14
  from cryptography.hazmat.primitives import serialization, hashes
@@ -15,6 +16,15 @@ from cryptography.hazmat.primitives.asymmetric import rsa
15
16
  from cryptography.hazmat.backends import default_backend
16
17
 
17
18
 
19
+ class AuthJSONSerializer(JSONEncoder):
20
+ def default(self, o):
21
+ if isinstance(o, datetime.datetime):
22
+ return o.isoformat()
23
+ else:
24
+ raise TypeError(f'Object of type {o.__class__.__name__} '
25
+ f'is not JSON serializable')
26
+
27
+
18
28
  class Auth:
19
29
  secret = None
20
30
  private_key = None
@@ -22,6 +32,28 @@ class Auth:
22
32
  loaded_private_key = None
23
33
  loaded_public_key = None
24
34
 
35
+ def hash_password(self, text):
36
+ """
37
+ Generates a Bcrypt password hash
38
+ :param text:
39
+ :return:
40
+ """
41
+ password_bytes = text.encode('utf-8')
42
+ # Generate a salt
43
+ salt = bcrypt.gensalt()
44
+ # Hash the password
45
+ return bcrypt.hashpw(password_bytes, salt).decode('utf-8')
46
+
47
+ def check_password(self, password_hash, text):
48
+ """
49
+ Checks a Bcrypt password hash
50
+ :param password_hash:
51
+ :param text:
52
+ :return:
53
+ """
54
+ password_bytes = text.encode('utf-8')
55
+ return bcrypt.checkpw(password_bytes, password_hash.encode('utf-8'))
56
+
25
57
  def load_private_key(self):
26
58
  if self.loaded_private_key:
27
59
  return self.loaded_private_key
@@ -104,16 +136,22 @@ class Auth:
104
136
  with open(self.self_signed, "wb") as f:
105
137
  f.write(cert.public_bytes(serialization.Encoding.PEM))
106
138
 
107
- def get_token(self, payload_data):
139
+ def get_token(self, payload_data, expiry_minutes=0):
108
140
  private_key = self.load_private_key()
109
141
  now = datetime.datetime.now()
110
- token_limit_minutes = os.environ.get("TINA4_TOKEN_LIMIT", 2)
111
- expiry_time = now + datetime.timedelta(minutes=token_limit_minutes)
112
- payload_data["expires"] = expiry_time.isoformat()
142
+
143
+ if not "expires" in payload_data:
144
+ token_limit_minutes = int(os.environ.get("TINA4_TOKEN_LIMIT", 2))
145
+ if expiry_minutes != 0:
146
+ token_limit_minutes = expiry_minutes
147
+ expiry_time = now + datetime.timedelta(minutes=token_limit_minutes)
148
+ payload_data["expires"] = expiry_time.isoformat()
149
+
113
150
  token = jwt.encode(
114
151
  payload=payload_data,
115
152
  key=private_key,
116
- algorithm='RS256'
153
+ algorithm='RS256',
154
+ json_encoder=AuthJSONSerializer
117
155
  )
118
156
 
119
157
  return token
@@ -26,10 +26,14 @@ HTTP_BAD_REQUEST = 400
26
26
  HTTP_UNAUTHORIZED = 401
27
27
  HTTP_FORBIDDEN = 403
28
28
  HTTP_NOT_FOUND = 404
29
+ HTTP_REDIRECT = 302
30
+ HTTP_SERVER_ERROR = 500
29
31
 
30
32
  LOOKUP_HTTP_CODE = {HTTP_OK: "OK", HTTP_CREATED: "Created", HTTP_BAD_REQUEST: "Bad Request",
31
33
  HTTP_PARTIAL_CONTENT: "Partial Content", HTTP_UNAUTHORIZED: "Unauthorized",
32
- HTTP_FORBIDDEN: "Forbidden", HTTP_NOT_FOUND: "Not Found", HTTP_NO_CONTENT: "No Content"}
34
+ HTTP_FORBIDDEN: "Forbidden", HTTP_NOT_FOUND: "Not Found", HTTP_NO_CONTENT: "No Content",
35
+ HTTP_REDIRECT: "Redirect",
36
+ HTTP_SERVER_ERROR: "Internal Server Error"}
33
37
 
34
38
  TEXT_HTML = "text/html"
35
39
  TEXT_CSS = "text/css"