the37lab-authlib 0.1.1751357568__tar.gz → 0.1.1756367559__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.

Potentially problematic release.


This version of the37lab-authlib might be problematic. Click here for more details.

Files changed (15) hide show
  1. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/PKG-INFO +38 -1
  2. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/README.md +37 -0
  3. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/pyproject.toml +1 -1
  4. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib/auth.py +44 -3
  5. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib/db.py +21 -4
  6. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib.egg-info/PKG-INFO +38 -1
  7. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/setup.cfg +0 -0
  8. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib/__init__.py +0 -0
  9. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib/decorators.py +0 -0
  10. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib/exceptions.py +0 -0
  11. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib/models.py +0 -0
  12. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib.egg-info/SOURCES.txt +0 -0
  13. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib.egg-info/dependency_links.txt +0 -0
  14. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib.egg-info/requires.txt +0 -0
  15. {the37lab_authlib-0.1.1751357568 → the37lab_authlib-0.1.1756367559}/src/the37lab_authlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: the37lab_authlib
3
- Version: 0.1.1751357568
3
+ Version: 0.1.1756367559
4
4
  Summary: Python SDK for the Authlib
5
5
  Author-email: the37lab <info@the37lab.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -39,6 +39,12 @@ A Python authentication library that provides JWT, OAuth2, and API token authent
39
39
  - [Setup](#setup)
40
40
  - [Database Setup](#database-setup)
41
41
  - [Running Tests](#running-tests)
42
+ - [API Token Override for Testing](#api-token-override-for-testing)
43
+ - [Usage](#usage)
44
+ - [Warning](#warning)
45
+ - [User Override for Testing](#user-override-for-testing)
46
+ - [Usage](#usage-1)
47
+ - [Warning](#warning-1)
42
48
 
43
49
  ## Installation
44
50
 
@@ -211,3 +217,34 @@ python -m authlib.cli db init
211
217
  ```bash
212
218
  pytest
213
219
  ```
220
+
221
+ ## API Token Override for Testing
222
+
223
+ For testing purposes, you can bypass the database and provide a static mapping of API tokens to usernames using the `api_tokens` argument to `AuthManager` or the `{PREFIX}API_TOKENS` environment variable.
224
+
225
+ ### Usage
226
+
227
+ - **Constructor argument:**
228
+ ```python
229
+ AuthManager(api_tokens={"token1": "user1", "token2": "user2"})
230
+ ```
231
+ - **Environment variable:**
232
+ Set `{PREFIX}API_TOKENS` to a comma-separated list of `token:username` pairs, e.g.:
233
+ ```
234
+ export MYAPP_API_TOKENS="token1:user1,token2:user2"
235
+ ```
236
+ Replace `MYAPP` with your environment prefix.
237
+
238
+ **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.
239
+
240
+ ## User Override for Testing
241
+
242
+ For testing purposes, you can force all authentication to return a specific user by setting the `{PREFIX}USER_OVERRIDE` environment variable:
243
+
244
+ ```bash
245
+ export MYAPP_USER_OVERRIDE="testuser"
246
+ ```
247
+
248
+ If set, all requests will be authenticated as the specified user, regardless of any tokens or credentials provided. This cannot be combined with `api_tokens` or `db_dsn`.
249
+
250
+ **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.
@@ -22,6 +22,12 @@ A Python authentication library that provides JWT, OAuth2, and API token authent
22
22
  - [Setup](#setup)
23
23
  - [Database Setup](#database-setup)
24
24
  - [Running Tests](#running-tests)
25
+ - [API Token Override for Testing](#api-token-override-for-testing)
26
+ - [Usage](#usage)
27
+ - [Warning](#warning)
28
+ - [User Override for Testing](#user-override-for-testing)
29
+ - [Usage](#usage-1)
30
+ - [Warning](#warning-1)
25
31
 
26
32
  ## Installation
27
33
 
@@ -194,3 +200,34 @@ python -m authlib.cli db init
194
200
  ```bash
195
201
  pytest
196
202
  ```
203
+
204
+ ## API Token Override for Testing
205
+
206
+ For testing purposes, you can bypass the database and provide a static mapping of API tokens to usernames using the `api_tokens` argument to `AuthManager` or the `{PREFIX}API_TOKENS` environment variable.
207
+
208
+ ### Usage
209
+
210
+ - **Constructor argument:**
211
+ ```python
212
+ AuthManager(api_tokens={"token1": "user1", "token2": "user2"})
213
+ ```
214
+ - **Environment variable:**
215
+ Set `{PREFIX}API_TOKENS` to a comma-separated list of `token:username` pairs, e.g.:
216
+ ```
217
+ export MYAPP_API_TOKENS="token1:user1,token2:user2"
218
+ ```
219
+ Replace `MYAPP` with your environment prefix.
220
+
221
+ **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.
222
+
223
+ ## User Override for Testing
224
+
225
+ For testing purposes, you can force all authentication to return a specific user by setting the `{PREFIX}USER_OVERRIDE` environment variable:
226
+
227
+ ```bash
228
+ export MYAPP_USER_OVERRIDE="testuser"
229
+ ```
230
+
231
+ If set, all requests will be authenticated as the specified user, regardless of any tokens or credentials provided. This cannot be combined with `api_tokens` or `db_dsn`.
232
+
233
+ **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "the37lab_authlib"
7
- version = "0.1.1751357568"
7
+ version = "0.1.1756367559"
8
8
  description = "Python SDK for the Authlib"
9
9
  authors = [{name = "the37lab", email = "info@the37lab.com"}]
10
10
  dependencies = ["flask", "psycopg2-binary", "pyjwt", "python-dotenv", "requests", "authlib", "bcrypt"]
@@ -11,12 +11,14 @@ import bcrypt
11
11
  import logging
12
12
  import os
13
13
  from functools import wraps
14
+ from isodate import parse_duration
14
15
 
15
16
  logging.basicConfig(level=logging.DEBUG)
16
17
  logger = logging.getLogger(__name__)
17
18
 
18
19
  class AuthManager:
19
- def __init__(self, app=None, db_dsn=None, jwt_secret=None, oauth_config=None, id_type='integer', environment_prefix=None):
20
+ def __init__(self, app=None, db_dsn=None, jwt_secret=None, oauth_config=None, id_type='integer', environment_prefix=None, api_tokens=None):
21
+ self.user_override = None
20
22
  if environment_prefix:
21
23
  prefix = environment_prefix.upper() + '_'
22
24
  db_dsn = os.getenv(f'{prefix}DATABASE_URL')
@@ -29,6 +31,25 @@ class AuthManager:
29
31
  'client_id': google_client_id,
30
32
  'client_secret': google_client_secret
31
33
  }
34
+ api_tokens_env = os.getenv(f'{prefix}API_TOKENS')
35
+ if api_tokens_env:
36
+ api_tokens = {}
37
+ for entry in api_tokens_env.split(','):
38
+ if ':' in entry:
39
+ key, user = entry.split(':', 1)
40
+ api_tokens[key.strip()] = user.strip()
41
+ user_override_env = os.getenv(f'{prefix}USER_OVERRIDE')
42
+ if user_override_env:
43
+ self.user_override = user_override_env
44
+ else:
45
+ prefix = ''
46
+
47
+ self.expiry_time = parse_duration(os.getenv(f'{prefix}JWT_TOKEN_EXPIRY_TIME', 'PT1H'))
48
+ if self.user_override and (api_tokens or db_dsn):
49
+ raise ValueError('Cannot set user_override together with api_tokens or db_dsn')
50
+ if api_tokens and db_dsn:
51
+ raise ValueError('Cannot set both api_tokens and db_dsn')
52
+ self.api_tokens = api_tokens or None
32
53
  self.db = Database(db_dsn, id_type=id_type) if db_dsn else None
33
54
  self.jwt_secret = jwt_secret
34
55
  self.oauth_config = oauth_config or {}
@@ -61,6 +82,18 @@ class AuthManager:
61
82
  return redirect_uri
62
83
 
63
84
  def _validate_api_token(self, api_token):
85
+ if self.api_tokens is not None:
86
+ username = self.api_tokens.get(api_token)
87
+ if not username:
88
+ raise AuthError('Invalid API token')
89
+ # Return a minimal user dict
90
+ return {
91
+ 'id': username,
92
+ 'username': username,
93
+ 'email': '',
94
+ 'real_name': username,
95
+ 'roles': []
96
+ }
64
97
  try:
65
98
  parsed = ApiToken.parse_token(api_token)
66
99
  with self.db.get_cursor() as cur:
@@ -109,6 +142,14 @@ class AuthManager:
109
142
  raise AuthError('Invalid token format')
110
143
 
111
144
  def _authenticate_request(self):
145
+ if self.user_override:
146
+ return {
147
+ 'id': self.user_override,
148
+ 'username': self.user_override,
149
+ 'email': '',
150
+ 'real_name': self.user_override,
151
+ 'roles': []
152
+ }
112
153
  auth_header = request.headers.get('Authorization')
113
154
  api_token = request.headers.get('X-API-Token')
114
155
 
@@ -451,12 +492,12 @@ class AuthManager:
451
492
  def _create_token(self, user):
452
493
  payload = {
453
494
  'sub': str(user['id']),
454
- 'exp': datetime.utcnow() + timedelta(hours=1),
495
+ 'exp': datetime.utcnow() + self.expiry_time,
455
496
  'iat': datetime.utcnow()
456
497
  }
457
498
  logger.debug(f"Creating token with payload: {payload}")
458
499
  token = jwt.encode(payload, self.jwt_secret, algorithm='HS256')
459
- logger.debug(f"Created token: {token}")
500
+ logger.info(f"Created token: {token}")
460
501
  return token
461
502
 
462
503
  def _create_refresh_token(self, user):
@@ -1,15 +1,28 @@
1
1
  import psycopg2
2
2
  from psycopg2.extras import RealDictCursor
3
+ from psycopg2 import pool
3
4
  from contextlib import contextmanager
4
5
  from .models import UUIDGenerator, IntegerGenerator
5
6
 
6
7
  class Database:
7
- def __init__(self, dsn, id_type='uuid'):
8
+ def __init__(self, dsn, id_type='uuid', min_conn=1, max_conn=10):
8
9
  self.dsn = dsn
9
10
  self.id_generator = UUIDGenerator() if id_type == 'uuid' else IntegerGenerator()
10
11
  self.id_type = id_type
12
+ self.min_conn = min_conn
13
+ self.max_conn = max_conn
14
+ self._pool = None
15
+ self._init_pool()
11
16
  self._init_db()
12
17
 
18
+ def _init_pool(self):
19
+ self._pool = pool.ThreadedConnectionPool(
20
+ self.min_conn,
21
+ self.max_conn,
22
+ self.dsn,
23
+ cursor_factory=RealDictCursor
24
+ )
25
+
13
26
  def _init_db(self):
14
27
  with self.get_connection() as conn:
15
28
  with conn.cursor() as cur:
@@ -54,7 +67,7 @@ class Database:
54
67
 
55
68
  @contextmanager
56
69
  def get_connection(self):
57
- conn = psycopg2.connect(self.dsn, cursor_factory=RealDictCursor)
70
+ conn = self._pool.getconn()
58
71
  try:
59
72
  yield conn
60
73
  conn.commit()
@@ -62,7 +75,7 @@ class Database:
62
75
  conn.rollback()
63
76
  raise
64
77
  finally:
65
- conn.close()
78
+ self._pool.putconn(conn)
66
79
 
67
80
  @contextmanager
68
81
  def get_cursor(self):
@@ -71,4 +84,8 @@ class Database:
71
84
  yield cur
72
85
 
73
86
  def get_id_generator(self):
74
- return self.id_generator
87
+ return self.id_generator
88
+
89
+ def close(self):
90
+ if self._pool:
91
+ self._pool.closeall()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: the37lab_authlib
3
- Version: 0.1.1751357568
3
+ Version: 0.1.1756367559
4
4
  Summary: Python SDK for the Authlib
5
5
  Author-email: the37lab <info@the37lab.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -39,6 +39,12 @@ A Python authentication library that provides JWT, OAuth2, and API token authent
39
39
  - [Setup](#setup)
40
40
  - [Database Setup](#database-setup)
41
41
  - [Running Tests](#running-tests)
42
+ - [API Token Override for Testing](#api-token-override-for-testing)
43
+ - [Usage](#usage)
44
+ - [Warning](#warning)
45
+ - [User Override for Testing](#user-override-for-testing)
46
+ - [Usage](#usage-1)
47
+ - [Warning](#warning-1)
42
48
 
43
49
  ## Installation
44
50
 
@@ -211,3 +217,34 @@ python -m authlib.cli db init
211
217
  ```bash
212
218
  pytest
213
219
  ```
220
+
221
+ ## API Token Override for Testing
222
+
223
+ For testing purposes, you can bypass the database and provide a static mapping of API tokens to usernames using the `api_tokens` argument to `AuthManager` or the `{PREFIX}API_TOKENS` environment variable.
224
+
225
+ ### Usage
226
+
227
+ - **Constructor argument:**
228
+ ```python
229
+ AuthManager(api_tokens={"token1": "user1", "token2": "user2"})
230
+ ```
231
+ - **Environment variable:**
232
+ Set `{PREFIX}API_TOKENS` to a comma-separated list of `token:username` pairs, e.g.:
233
+ ```
234
+ export MYAPP_API_TOKENS="token1:user1,token2:user2"
235
+ ```
236
+ Replace `MYAPP` with your environment prefix.
237
+
238
+ **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.
239
+
240
+ ## User Override for Testing
241
+
242
+ For testing purposes, you can force all authentication to return a specific user by setting the `{PREFIX}USER_OVERRIDE` environment variable:
243
+
244
+ ```bash
245
+ export MYAPP_USER_OVERRIDE="testuser"
246
+ ```
247
+
248
+ If set, all requests will be authenticated as the specified user, regardless of any tokens or credentials provided. This cannot be combined with `api_tokens` or `db_dsn`.
249
+
250
+ **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.