azure-postgresql-auth 1.0.0__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.
- azure_postgresql_auth-1.0.0/PKG-INFO +360 -0
- azure_postgresql_auth-1.0.0/README.md +311 -0
- azure_postgresql_auth-1.0.0/pyproject.toml +104 -0
- azure_postgresql_auth-1.0.0/setup.cfg +4 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/__init__.py +20 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/core.py +244 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/errors.py +36 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/psycopg2/__init__.py +25 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/psycopg2/entra_connection.py +78 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/psycopg3/__init__.py +24 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/psycopg3/async_entra_connection.py +66 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/psycopg3/entra_connection.py +66 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/py.typed +0 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/sqlalchemy/__init__.py +28 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/sqlalchemy/async_entra_connection.py +73 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth/sqlalchemy/entra_connection.py +70 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth.egg-info/PKG-INFO +360 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth.egg-info/SOURCES.txt +19 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth.egg-info/dependency_links.txt +1 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth.egg-info/requires.txt +40 -0
- azure_postgresql_auth-1.0.0/src/azure_postgresql_auth.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: azure-postgresql-auth
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Azure Entra ID authentication extension for Python database drivers
|
|
5
|
+
Author-email: Arjun Narendra <v-anarendra@microsoft.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/v-anarendra_microsoft/entra-id-integration-for-drivers
|
|
8
|
+
Project-URL: Issues, https://github.com/v-anarendra_microsoft/entra-id-integration-for-drivers/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: azure-identity>=1.13.0
|
|
15
|
+
Requires-Dist: azure-core>=1.24.0
|
|
16
|
+
Provides-Extra: psycopg3
|
|
17
|
+
Requires-Dist: psycopg[binary]>=3.1.0; extra == "psycopg3"
|
|
18
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "psycopg3"
|
|
19
|
+
Provides-Extra: psycopg2
|
|
20
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "psycopg2"
|
|
21
|
+
Provides-Extra: sqlalchemy
|
|
22
|
+
Requires-Dist: sqlalchemy>=2.0.0; extra == "sqlalchemy"
|
|
23
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "sqlalchemy"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
27
|
+
Requires-Dist: python-dotenv>=1.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy~=1.15; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.8.0; extra == "dev"
|
|
30
|
+
Requires-Dist: types-psycopg2>=2.9.0; extra == "dev"
|
|
31
|
+
Requires-Dist: psycopg-pool>=3.1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: testcontainers[postgres]>=3.7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: psycopg[binary]>=3.1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "dev"
|
|
35
|
+
Requires-Dist: sqlalchemy>=2.0.0; extra == "dev"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: psycopg[binary]>=3.1.0; extra == "all"
|
|
38
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "all"
|
|
39
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "all"
|
|
40
|
+
Requires-Dist: sqlalchemy>=2.0.0; extra == "all"
|
|
41
|
+
Requires-Dist: pytest>=7.0.0; extra == "all"
|
|
42
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
|
|
43
|
+
Requires-Dist: python-dotenv>=1.0.0; extra == "all"
|
|
44
|
+
Requires-Dist: mypy~=1.15; extra == "all"
|
|
45
|
+
Requires-Dist: ruff>=0.8.0; extra == "all"
|
|
46
|
+
Requires-Dist: types-psycopg2>=2.9.0; extra == "all"
|
|
47
|
+
Requires-Dist: psycopg-pool>=3.1.0; extra == "all"
|
|
48
|
+
Requires-Dist: testcontainers[postgres]>=3.7.0; extra == "all"
|
|
49
|
+
|
|
50
|
+
# azurepg-entra: Azure Database for PostgreSQL Entra ID Authentication
|
|
51
|
+
|
|
52
|
+
This package provides seamless Azure Entra ID authentication for Python database drivers connecting to Azure Database for PostgreSQL. It supports both legacy and modern PostgreSQL drivers with automatic token management and connection pooling.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- **🔐 Azure Entra ID Authentication**: Automatic token acquisition and refresh for secure database connections
|
|
57
|
+
- **🔄 Multi-Driver Support**: Works with psycopg2, psycopg3, and SQLAlchemy
|
|
58
|
+
- **⚡ Connection Pooling**: Built-in support for both synchronous and asynchronous connection pools
|
|
59
|
+
- **🏗️ Clean Architecture**: Simple package structure with `azure_postgresql_auth.psycopg2`, `azure_postgresql_auth.psycopg3`, and `azure_postgresql_auth.sqlalchemy`
|
|
60
|
+
- **🔄 Automatic Token Management**: Handles token acquisition, validation, and refresh automatically
|
|
61
|
+
- **🌐 Cross-platform**: Works on Windows, Linux, and macOS
|
|
62
|
+
- **📦 Flexible Installation**: Optional dependencies for different driver combinations
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
### Basic Installation
|
|
67
|
+
|
|
68
|
+
Install the core package (includes Azure Identity dependencies only):
|
|
69
|
+
```bash
|
|
70
|
+
pip install azurepg-entra
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Driver-Specific Installation
|
|
74
|
+
|
|
75
|
+
Choose the installation option based on which PostgreSQL drivers you need:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# For psycopg3 (modern psycopg, recommended for new projects)
|
|
79
|
+
pip install "azurepg-entra[psycopg3]"
|
|
80
|
+
|
|
81
|
+
# For psycopg2 (legacy support)
|
|
82
|
+
pip install "azurepg-entra[psycopg2]"
|
|
83
|
+
|
|
84
|
+
# For SQLAlchemy with psycopg3 backend
|
|
85
|
+
pip install "azurepg-entra[sqlalchemy]"
|
|
86
|
+
|
|
87
|
+
# All database drivers combined
|
|
88
|
+
pip install "azurepg-entra[drivers]"
|
|
89
|
+
|
|
90
|
+
# Everything including development tools
|
|
91
|
+
pip install "azurepg-entra[all]"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Development Installation
|
|
95
|
+
|
|
96
|
+
Install from source for development:
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/v-anarendra_microsoft/entra-id-integration-for-drivers.git
|
|
99
|
+
cd entra-id-integration-for-drivers/python
|
|
100
|
+
|
|
101
|
+
# Install with all dependencies for development
|
|
102
|
+
pip install -e ".[all]"
|
|
103
|
+
|
|
104
|
+
# Or install specific driver combinations
|
|
105
|
+
pip install -e ".[psycopg3,dev]"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
### Environment Variables
|
|
111
|
+
|
|
112
|
+
The samples use environment variables to configure database connections.
|
|
113
|
+
|
|
114
|
+
Copy `.env.example` into a `.env` file in the same directory and update the variables.
|
|
115
|
+
```env
|
|
116
|
+
POSTGRES_SERVER=<your-server.postgres.database.azure.com>
|
|
117
|
+
POSTGRES_DATABASE=<your_database_name>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Quick Start
|
|
121
|
+
|
|
122
|
+
### Running the Samples
|
|
123
|
+
|
|
124
|
+
The repository includes comprehensive working examples in the `samples/` directory:
|
|
125
|
+
|
|
126
|
+
- **`samples/psycopg2/getting_started/`**: psycopg2 (legacy driver support)
|
|
127
|
+
- **`samples/psycopg3/getting_started/`**: psycopg3 examples (modern driver, recommended)
|
|
128
|
+
- **`samples/sqlalchemy/getting_started/`**: SQLAlchemy examples with psycopg3 backend
|
|
129
|
+
|
|
130
|
+
Configure your environment variables first, then run the samples:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Copy and configure environment
|
|
134
|
+
cp samples/psycopg3/getting_started/.env.example samples/psycopg3/getting_started/.env
|
|
135
|
+
# Edit .env with your Azure PostgreSQL server details
|
|
136
|
+
|
|
137
|
+
# Test psycopg2 (legacy driver)
|
|
138
|
+
python samples/psycopg2/getting_started/create_db_connection_psycopg2.py --mode both
|
|
139
|
+
|
|
140
|
+
# Test psycopg3 (modern driver, recommended)
|
|
141
|
+
python samples/psycopg3/getting_started/create_db_connection_psycopg.py --mode both
|
|
142
|
+
|
|
143
|
+
# Test SQLAlchemy
|
|
144
|
+
python samples/sqlalchemy/getting_started/create_db_connection_sqlalchemy.py --mode both
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Usage
|
|
148
|
+
|
|
149
|
+
Choose the driver that best fits your project needs:
|
|
150
|
+
|
|
151
|
+
- **psycopg3**: Modern PostgreSQL driver (recommended for new projects)
|
|
152
|
+
- **psycopg2**: Legacy PostgreSQL driver (for existing projects)
|
|
153
|
+
- **SQLAlchemy**: High-level ORM/Core interface
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## psycopg2 Driver (Legacy Support)
|
|
158
|
+
|
|
159
|
+
> **Note**: psycopg2 is in maintenance mode. For new projects, consider using psycopg3 instead.
|
|
160
|
+
|
|
161
|
+
The psycopg2 integration provides synchronous connection support with Azure Entra ID authentication through connection pooling.
|
|
162
|
+
|
|
163
|
+
### Installation
|
|
164
|
+
```bash
|
|
165
|
+
pip install "azurepg-entra[psycopg2]"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Connection Pooling (Recommended)
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from azure_postgresql_auth.psycopg2 import EntraConnection # import library
|
|
172
|
+
from psycopg2 import pool # import to use pooling
|
|
173
|
+
|
|
174
|
+
with pool.ThreadedConnectionPool(
|
|
175
|
+
minconn=1,
|
|
176
|
+
maxconn=5,
|
|
177
|
+
host="your-server.postgres.database.azure.com",
|
|
178
|
+
database="your_database",
|
|
179
|
+
connection_factory=EntraConnection
|
|
180
|
+
) as connection_pool:
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Direct Connection
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from azure_postgresql_auth.psycopg2 import EntraConnection # import library
|
|
187
|
+
|
|
188
|
+
with EntraConnection(
|
|
189
|
+
"postgresql://your-server.postgres.database.azure.com:5432/your_database"
|
|
190
|
+
) as conn
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## psycopg3 Driver (Recommended)
|
|
196
|
+
|
|
197
|
+
psycopg3 is the modern, actively developed PostgreSQL driver with native async support and better performance.
|
|
198
|
+
|
|
199
|
+
### Installation
|
|
200
|
+
```bash
|
|
201
|
+
pip install "azurepg-entra[psycopg3]"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Synchronous Connection
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from azure_postgresql_auth.psycopg3 import EntraConnection # import library
|
|
208
|
+
from psycopg_pool import ConnectionPool # import to use pooling
|
|
209
|
+
|
|
210
|
+
with ConnectionPool(
|
|
211
|
+
conninfo="postgresql://your-server.postgres.database.azure.com:5432/your_database",
|
|
212
|
+
connection_class=EntraConnection,
|
|
213
|
+
min_size=1, # keep at least 1 connection always open
|
|
214
|
+
max_size=5, # allow up to 5 concurrent connections
|
|
215
|
+
) as pool
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Asynchronous Connection
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from azure_postgresql_auth.psycopg3 import AsyncEntraConnection # import library
|
|
222
|
+
from psycopg_pool import AsyncConnectionPool # import to use pooling
|
|
223
|
+
|
|
224
|
+
async with AsyncConnectionPool(
|
|
225
|
+
conninfo="postgresql://your-server.postgres.database.azure.com:5432/your_database",
|
|
226
|
+
connection_class=AsyncEntraConnection,
|
|
227
|
+
min_size=1, # keep at least 1 connection always open
|
|
228
|
+
max_size=5, # allow up to 5 concurrent connections
|
|
229
|
+
) as pool
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## SQLAlchemy Integration
|
|
235
|
+
|
|
236
|
+
SQLAlchemy integration uses psycopg3 as the backend driver with automatic Entra ID authentication through event listeners.
|
|
237
|
+
|
|
238
|
+
> **For more information**: See SQLAlchemy's documentation on [controlling how parameters are passed to the DBAPI connect function](https://docs.sqlalchemy.org/en/20/core/engines.html#controlling-how-parameters-are-passed-to-the-dbapi-connect-function).
|
|
239
|
+
|
|
240
|
+
### Installation
|
|
241
|
+
```bash
|
|
242
|
+
pip install "azurepg-entra[sqlalchemy]"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Synchronous Engine
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from sqlalchemy import create_engine
|
|
249
|
+
from azure_postgresql_auth.sqlalchemy import enable_entra_authentication # import library
|
|
250
|
+
|
|
251
|
+
with create_engine("postgresql+psycopg://your-server.postgres.database.azure.com/your_database") as engine:
|
|
252
|
+
# Enable Entra ID authentication
|
|
253
|
+
enable_entra_authentication(engine)
|
|
254
|
+
|
|
255
|
+
# Core usage
|
|
256
|
+
with engine.connect() as conn:
|
|
257
|
+
|
|
258
|
+
# ORM usage
|
|
259
|
+
from sqlalchemy.orm import sessionmaker
|
|
260
|
+
Session = sessionmaker(bind=engine)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Asynchronous Engine
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
267
|
+
from azure_postgresql_auth.sqlalchemy import enable_entra_authentication_async # import library
|
|
268
|
+
|
|
269
|
+
async with create_async_engine("postgresql+psycopg://your-server.postgres.database.azure.com/your_database") as engine:
|
|
270
|
+
# Enable Entra ID authentication for async
|
|
271
|
+
enable_entra_authentication_async(engine)
|
|
272
|
+
|
|
273
|
+
# Async Core usage
|
|
274
|
+
async with engine.connect() as conn:
|
|
275
|
+
|
|
276
|
+
# Async ORM usage
|
|
277
|
+
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
278
|
+
AsyncSession = async_sessionmaker(engine, expire_on_commit=False)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## How It Works
|
|
282
|
+
|
|
283
|
+
### Authentication Flow
|
|
284
|
+
|
|
285
|
+
1. **Token Acquisition**: Uses Azure Identity libraries (`DefaultAzureCredential` by default) to acquire access tokens from Azure Entra ID
|
|
286
|
+
2. **Automatic Refresh**: Tokens are automatically refreshed before each new database connection
|
|
287
|
+
3. **Secure Transport**: Tokens are passed as passwords in PostgreSQL connection strings over SSL
|
|
288
|
+
4. **Server Validation**: Azure Database for PostgreSQL validates the token and establishes the authenticated connection
|
|
289
|
+
5. **User Mapping**: The token's user principal name (UPN) is mapped to a PostgreSQL user for authorization
|
|
290
|
+
|
|
291
|
+
### Token Scopes
|
|
292
|
+
|
|
293
|
+
The package automatically requests the correct OAuth2 scopes:
|
|
294
|
+
- **Database scope**: `https://ossrdbms-aad.database.windows.net/.default` (primary)
|
|
295
|
+
- **Management scope**: `https://management.azure.com/.default` (fallback for managed identities)
|
|
296
|
+
|
|
297
|
+
### Security Features
|
|
298
|
+
|
|
299
|
+
- **🔒 Token-based authentication**: No passwords stored or transmitted
|
|
300
|
+
- **⏰ Automatic expiration**: Tokens expire and are refreshed automatically
|
|
301
|
+
- **🛡️ SSL enforcement**: All connections require SSL encryption
|
|
302
|
+
- **🔑 Principle of least privilege**: Only database-specific scopes are requested
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Troubleshooting
|
|
306
|
+
|
|
307
|
+
### Common Issues
|
|
308
|
+
|
|
309
|
+
**Authentication Errors**
|
|
310
|
+
```bash
|
|
311
|
+
# Error: "password authentication failed"
|
|
312
|
+
# Solution: Ensure your Azure identity has been granted access to the database
|
|
313
|
+
# Run this SQL as a database administrator:
|
|
314
|
+
CREATE ROLE "your-user@your-domain.com" WITH LOGIN;
|
|
315
|
+
GRANT ALL PRIVILEGES ON DATABASE your_database TO "your-user@your-domain.com";
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Connection Timeouts**
|
|
319
|
+
```python
|
|
320
|
+
# Increase connection timeout for slow networks
|
|
321
|
+
conn = SyncEntraConnection.connect(
|
|
322
|
+
"postgresql://server:5432/db",
|
|
323
|
+
connect_timeout=30 # 30 seconds instead of default 10
|
|
324
|
+
)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Windows Async Issues**
|
|
328
|
+
```python
|
|
329
|
+
# Fix Windows event loop compatibility
|
|
330
|
+
import asyncio
|
|
331
|
+
import sys
|
|
332
|
+
|
|
333
|
+
if sys.platform == "win32":
|
|
334
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Debug Logging
|
|
338
|
+
|
|
339
|
+
Enable debug logging to troubleshoot authentication issues:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
import logging
|
|
343
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
344
|
+
|
|
345
|
+
# This will show token acquisition and connection details
|
|
346
|
+
conn = SyncEntraConnection.connect("postgresql://server:5432/db")
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Contributing
|
|
352
|
+
|
|
353
|
+
We welcome contributions! Please see [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines.
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## License
|
|
359
|
+
|
|
360
|
+
This project is licensed under the MIT License - see the [LICENSE](../LICENSE) file for details.
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# azurepg-entra: Azure Database for PostgreSQL Entra ID Authentication
|
|
2
|
+
|
|
3
|
+
This package provides seamless Azure Entra ID authentication for Python database drivers connecting to Azure Database for PostgreSQL. It supports both legacy and modern PostgreSQL drivers with automatic token management and connection pooling.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **🔐 Azure Entra ID Authentication**: Automatic token acquisition and refresh for secure database connections
|
|
8
|
+
- **🔄 Multi-Driver Support**: Works with psycopg2, psycopg3, and SQLAlchemy
|
|
9
|
+
- **⚡ Connection Pooling**: Built-in support for both synchronous and asynchronous connection pools
|
|
10
|
+
- **🏗️ Clean Architecture**: Simple package structure with `azure_postgresql_auth.psycopg2`, `azure_postgresql_auth.psycopg3`, and `azure_postgresql_auth.sqlalchemy`
|
|
11
|
+
- **🔄 Automatic Token Management**: Handles token acquisition, validation, and refresh automatically
|
|
12
|
+
- **🌐 Cross-platform**: Works on Windows, Linux, and macOS
|
|
13
|
+
- **📦 Flexible Installation**: Optional dependencies for different driver combinations
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### Basic Installation
|
|
18
|
+
|
|
19
|
+
Install the core package (includes Azure Identity dependencies only):
|
|
20
|
+
```bash
|
|
21
|
+
pip install azurepg-entra
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Driver-Specific Installation
|
|
25
|
+
|
|
26
|
+
Choose the installation option based on which PostgreSQL drivers you need:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# For psycopg3 (modern psycopg, recommended for new projects)
|
|
30
|
+
pip install "azurepg-entra[psycopg3]"
|
|
31
|
+
|
|
32
|
+
# For psycopg2 (legacy support)
|
|
33
|
+
pip install "azurepg-entra[psycopg2]"
|
|
34
|
+
|
|
35
|
+
# For SQLAlchemy with psycopg3 backend
|
|
36
|
+
pip install "azurepg-entra[sqlalchemy]"
|
|
37
|
+
|
|
38
|
+
# All database drivers combined
|
|
39
|
+
pip install "azurepg-entra[drivers]"
|
|
40
|
+
|
|
41
|
+
# Everything including development tools
|
|
42
|
+
pip install "azurepg-entra[all]"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Development Installation
|
|
46
|
+
|
|
47
|
+
Install from source for development:
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/v-anarendra_microsoft/entra-id-integration-for-drivers.git
|
|
50
|
+
cd entra-id-integration-for-drivers/python
|
|
51
|
+
|
|
52
|
+
# Install with all dependencies for development
|
|
53
|
+
pip install -e ".[all]"
|
|
54
|
+
|
|
55
|
+
# Or install specific driver combinations
|
|
56
|
+
pip install -e ".[psycopg3,dev]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
### Environment Variables
|
|
62
|
+
|
|
63
|
+
The samples use environment variables to configure database connections.
|
|
64
|
+
|
|
65
|
+
Copy `.env.example` into a `.env` file in the same directory and update the variables.
|
|
66
|
+
```env
|
|
67
|
+
POSTGRES_SERVER=<your-server.postgres.database.azure.com>
|
|
68
|
+
POSTGRES_DATABASE=<your_database_name>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
### Running the Samples
|
|
74
|
+
|
|
75
|
+
The repository includes comprehensive working examples in the `samples/` directory:
|
|
76
|
+
|
|
77
|
+
- **`samples/psycopg2/getting_started/`**: psycopg2 (legacy driver support)
|
|
78
|
+
- **`samples/psycopg3/getting_started/`**: psycopg3 examples (modern driver, recommended)
|
|
79
|
+
- **`samples/sqlalchemy/getting_started/`**: SQLAlchemy examples with psycopg3 backend
|
|
80
|
+
|
|
81
|
+
Configure your environment variables first, then run the samples:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Copy and configure environment
|
|
85
|
+
cp samples/psycopg3/getting_started/.env.example samples/psycopg3/getting_started/.env
|
|
86
|
+
# Edit .env with your Azure PostgreSQL server details
|
|
87
|
+
|
|
88
|
+
# Test psycopg2 (legacy driver)
|
|
89
|
+
python samples/psycopg2/getting_started/create_db_connection_psycopg2.py --mode both
|
|
90
|
+
|
|
91
|
+
# Test psycopg3 (modern driver, recommended)
|
|
92
|
+
python samples/psycopg3/getting_started/create_db_connection_psycopg.py --mode both
|
|
93
|
+
|
|
94
|
+
# Test SQLAlchemy
|
|
95
|
+
python samples/sqlalchemy/getting_started/create_db_connection_sqlalchemy.py --mode both
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Usage
|
|
99
|
+
|
|
100
|
+
Choose the driver that best fits your project needs:
|
|
101
|
+
|
|
102
|
+
- **psycopg3**: Modern PostgreSQL driver (recommended for new projects)
|
|
103
|
+
- **psycopg2**: Legacy PostgreSQL driver (for existing projects)
|
|
104
|
+
- **SQLAlchemy**: High-level ORM/Core interface
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## psycopg2 Driver (Legacy Support)
|
|
109
|
+
|
|
110
|
+
> **Note**: psycopg2 is in maintenance mode. For new projects, consider using psycopg3 instead.
|
|
111
|
+
|
|
112
|
+
The psycopg2 integration provides synchronous connection support with Azure Entra ID authentication through connection pooling.
|
|
113
|
+
|
|
114
|
+
### Installation
|
|
115
|
+
```bash
|
|
116
|
+
pip install "azurepg-entra[psycopg2]"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Connection Pooling (Recommended)
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from azure_postgresql_auth.psycopg2 import EntraConnection # import library
|
|
123
|
+
from psycopg2 import pool # import to use pooling
|
|
124
|
+
|
|
125
|
+
with pool.ThreadedConnectionPool(
|
|
126
|
+
minconn=1,
|
|
127
|
+
maxconn=5,
|
|
128
|
+
host="your-server.postgres.database.azure.com",
|
|
129
|
+
database="your_database",
|
|
130
|
+
connection_factory=EntraConnection
|
|
131
|
+
) as connection_pool:
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Direct Connection
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from azure_postgresql_auth.psycopg2 import EntraConnection # import library
|
|
138
|
+
|
|
139
|
+
with EntraConnection(
|
|
140
|
+
"postgresql://your-server.postgres.database.azure.com:5432/your_database"
|
|
141
|
+
) as conn
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## psycopg3 Driver (Recommended)
|
|
147
|
+
|
|
148
|
+
psycopg3 is the modern, actively developed PostgreSQL driver with native async support and better performance.
|
|
149
|
+
|
|
150
|
+
### Installation
|
|
151
|
+
```bash
|
|
152
|
+
pip install "azurepg-entra[psycopg3]"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Synchronous Connection
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from azure_postgresql_auth.psycopg3 import EntraConnection # import library
|
|
159
|
+
from psycopg_pool import ConnectionPool # import to use pooling
|
|
160
|
+
|
|
161
|
+
with ConnectionPool(
|
|
162
|
+
conninfo="postgresql://your-server.postgres.database.azure.com:5432/your_database",
|
|
163
|
+
connection_class=EntraConnection,
|
|
164
|
+
min_size=1, # keep at least 1 connection always open
|
|
165
|
+
max_size=5, # allow up to 5 concurrent connections
|
|
166
|
+
) as pool
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Asynchronous Connection
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from azure_postgresql_auth.psycopg3 import AsyncEntraConnection # import library
|
|
173
|
+
from psycopg_pool import AsyncConnectionPool # import to use pooling
|
|
174
|
+
|
|
175
|
+
async with AsyncConnectionPool(
|
|
176
|
+
conninfo="postgresql://your-server.postgres.database.azure.com:5432/your_database",
|
|
177
|
+
connection_class=AsyncEntraConnection,
|
|
178
|
+
min_size=1, # keep at least 1 connection always open
|
|
179
|
+
max_size=5, # allow up to 5 concurrent connections
|
|
180
|
+
) as pool
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## SQLAlchemy Integration
|
|
186
|
+
|
|
187
|
+
SQLAlchemy integration uses psycopg3 as the backend driver with automatic Entra ID authentication through event listeners.
|
|
188
|
+
|
|
189
|
+
> **For more information**: See SQLAlchemy's documentation on [controlling how parameters are passed to the DBAPI connect function](https://docs.sqlalchemy.org/en/20/core/engines.html#controlling-how-parameters-are-passed-to-the-dbapi-connect-function).
|
|
190
|
+
|
|
191
|
+
### Installation
|
|
192
|
+
```bash
|
|
193
|
+
pip install "azurepg-entra[sqlalchemy]"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Synchronous Engine
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from sqlalchemy import create_engine
|
|
200
|
+
from azure_postgresql_auth.sqlalchemy import enable_entra_authentication # import library
|
|
201
|
+
|
|
202
|
+
with create_engine("postgresql+psycopg://your-server.postgres.database.azure.com/your_database") as engine:
|
|
203
|
+
# Enable Entra ID authentication
|
|
204
|
+
enable_entra_authentication(engine)
|
|
205
|
+
|
|
206
|
+
# Core usage
|
|
207
|
+
with engine.connect() as conn:
|
|
208
|
+
|
|
209
|
+
# ORM usage
|
|
210
|
+
from sqlalchemy.orm import sessionmaker
|
|
211
|
+
Session = sessionmaker(bind=engine)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Asynchronous Engine
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
218
|
+
from azure_postgresql_auth.sqlalchemy import enable_entra_authentication_async # import library
|
|
219
|
+
|
|
220
|
+
async with create_async_engine("postgresql+psycopg://your-server.postgres.database.azure.com/your_database") as engine:
|
|
221
|
+
# Enable Entra ID authentication for async
|
|
222
|
+
enable_entra_authentication_async(engine)
|
|
223
|
+
|
|
224
|
+
# Async Core usage
|
|
225
|
+
async with engine.connect() as conn:
|
|
226
|
+
|
|
227
|
+
# Async ORM usage
|
|
228
|
+
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
229
|
+
AsyncSession = async_sessionmaker(engine, expire_on_commit=False)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## How It Works
|
|
233
|
+
|
|
234
|
+
### Authentication Flow
|
|
235
|
+
|
|
236
|
+
1. **Token Acquisition**: Uses Azure Identity libraries (`DefaultAzureCredential` by default) to acquire access tokens from Azure Entra ID
|
|
237
|
+
2. **Automatic Refresh**: Tokens are automatically refreshed before each new database connection
|
|
238
|
+
3. **Secure Transport**: Tokens are passed as passwords in PostgreSQL connection strings over SSL
|
|
239
|
+
4. **Server Validation**: Azure Database for PostgreSQL validates the token and establishes the authenticated connection
|
|
240
|
+
5. **User Mapping**: The token's user principal name (UPN) is mapped to a PostgreSQL user for authorization
|
|
241
|
+
|
|
242
|
+
### Token Scopes
|
|
243
|
+
|
|
244
|
+
The package automatically requests the correct OAuth2 scopes:
|
|
245
|
+
- **Database scope**: `https://ossrdbms-aad.database.windows.net/.default` (primary)
|
|
246
|
+
- **Management scope**: `https://management.azure.com/.default` (fallback for managed identities)
|
|
247
|
+
|
|
248
|
+
### Security Features
|
|
249
|
+
|
|
250
|
+
- **🔒 Token-based authentication**: No passwords stored or transmitted
|
|
251
|
+
- **⏰ Automatic expiration**: Tokens expire and are refreshed automatically
|
|
252
|
+
- **🛡️ SSL enforcement**: All connections require SSL encryption
|
|
253
|
+
- **🔑 Principle of least privilege**: Only database-specific scopes are requested
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Troubleshooting
|
|
257
|
+
|
|
258
|
+
### Common Issues
|
|
259
|
+
|
|
260
|
+
**Authentication Errors**
|
|
261
|
+
```bash
|
|
262
|
+
# Error: "password authentication failed"
|
|
263
|
+
# Solution: Ensure your Azure identity has been granted access to the database
|
|
264
|
+
# Run this SQL as a database administrator:
|
|
265
|
+
CREATE ROLE "your-user@your-domain.com" WITH LOGIN;
|
|
266
|
+
GRANT ALL PRIVILEGES ON DATABASE your_database TO "your-user@your-domain.com";
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Connection Timeouts**
|
|
270
|
+
```python
|
|
271
|
+
# Increase connection timeout for slow networks
|
|
272
|
+
conn = SyncEntraConnection.connect(
|
|
273
|
+
"postgresql://server:5432/db",
|
|
274
|
+
connect_timeout=30 # 30 seconds instead of default 10
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Windows Async Issues**
|
|
279
|
+
```python
|
|
280
|
+
# Fix Windows event loop compatibility
|
|
281
|
+
import asyncio
|
|
282
|
+
import sys
|
|
283
|
+
|
|
284
|
+
if sys.platform == "win32":
|
|
285
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Debug Logging
|
|
289
|
+
|
|
290
|
+
Enable debug logging to troubleshoot authentication issues:
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
import logging
|
|
294
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
295
|
+
|
|
296
|
+
# This will show token acquisition and connection details
|
|
297
|
+
conn = SyncEntraConnection.connect("postgresql://server:5432/db")
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Contributing
|
|
303
|
+
|
|
304
|
+
We welcome contributions! Please see [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines.
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## License
|
|
310
|
+
|
|
311
|
+
This project is licensed under the MIT License - see the [LICENSE](../LICENSE) file for details.
|