Commit cd80d9c
committed
Add ActiveDirectoryServicePrincipal support for bulk copy
Wires a Python token-factory callback into the mssql-py-core connection
context so bulk copy can authenticate with `Authentication=
ActiveDirectoryServicePrincipal`. The callback is invoked by mssql-tds
mid-handshake (FedAuth workflow 0x02), receives the STS URL from the
server, parses tenant_id from it, and uses azure-identity's
ClientSecretCredential to acquire a JWT — matching what ODBC does for
the query path.
Why a callback rather than pre-acquire (Model A):
- ServicePrincipal needs `tenant_id` to build ClientSecretCredential.
- The connection string does not carry it; azure-identity does not
discover it. The only place we can learn tenant_id client-side is
from the STS URL the server hands back during pre-login (which is
exactly what ODBC does internally).
- A pre-acquire flow therefore can't work. Saurabh approved the
callback model on EXP--Drivers--Python (May 13 2026).
Scope: ServicePrincipal only. ActiveDirectoryPassword and
ActiveDirectoryIntegrated remain on their existing code paths (still
rejected in py-core today with a clear error). Separate follow-ups.
Changes:
- constants.py: Add AuthType.SERVICE_PRINCIPAL.
- auth.py:
- Add ServicePrincipalAuth.make_token_factory(client_id, secret)
that returns a (spn, sts_url, auth_method) -> bytes callable
matching the entra_id_token_factory contract py-core expects.
- Add _parse_tenant_id() helper.
- process_auth_parameters: detect SP and return auth_type=None
(let ODBC handle the query path natively, msodbcsql 17.3+ has
native SP support).
- extract_auth_type: propagate "serviceprincipal" so bulkcopy can
distinguish the SP path.
- cursor.py bulkcopy: when _auth_type=="serviceprincipal", build the
factory from connection-string UID/PWD and register it via the new
entra_id_token_factory dict key. Keep authentication/user_name/
password in pycore_context (py-core's auth validator + transformer
need them to resolve the method to ActiveDirectoryServicePrincipal
before the factory is dispatched). Existing Default/DeviceCode/
Interactive (Model A) flow unchanged.
Requires mssql-py-core 0.1.5+ which wires the entra_id_token_factory
dict key into ClientContext.auth_method_map.
Tests: 17 new in test_008_auth.py covering tenant parsing
(GUID/domain/query-string/trailing-slash/empty/etc), credential kwarg
forwarding, scope construction, UTF-16LE encoding, and error paths
(missing client_id/secret, bad STS URL, auth failure propagation).
Partial fix for #534.1 parent c776ede commit cd80d9c
4 files changed
Lines changed: 335 additions & 18 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
130 | 130 | | |
131 | 131 | | |
132 | 132 | | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
133 | 230 | | |
134 | 231 | | |
135 | 232 | | |
| |||
180 | 277 | | |
181 | 278 | | |
182 | 279 | | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
183 | 293 | | |
184 | 294 | | |
185 | 295 | | |
| |||
246 | 356 | | |
247 | 357 | | |
248 | 358 | | |
| 359 | + | |
249 | 360 | | |
250 | 361 | | |
251 | 362 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
337 | 337 | | |
338 | 338 | | |
339 | 339 | | |
| 340 | + | |
340 | 341 | | |
341 | 342 | | |
342 | 343 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2934 | 2934 | | |
2935 | 2935 | | |
2936 | 2936 | | |
2937 | | - | |
2938 | | - | |
2939 | | - | |
2940 | | - | |
2941 | | - | |
2942 | | - | |
2943 | | - | |
2944 | | - | |
2945 | | - | |
2946 | | - | |
2947 | | - | |
2948 | | - | |
2949 | | - | |
2950 | | - | |
2951 | | - | |
2952 | | - | |
2953 | | - | |
2954 | | - | |
| 2937 | + | |
| 2938 | + | |
| 2939 | + | |
| 2940 | + | |
| 2941 | + | |
| 2942 | + | |
| 2943 | + | |
| 2944 | + | |
| 2945 | + | |
| 2946 | + | |
| 2947 | + | |
| 2948 | + | |
| 2949 | + | |
| 2950 | + | |
| 2951 | + | |
| 2952 | + | |
| 2953 | + | |
| 2954 | + | |
| 2955 | + | |
| 2956 | + | |
| 2957 | + | |
| 2958 | + | |
| 2959 | + | |
| 2960 | + | |
| 2961 | + | |
| 2962 | + | |
| 2963 | + | |
| 2964 | + | |
| 2965 | + | |
| 2966 | + | |
| 2967 | + | |
| 2968 | + | |
| 2969 | + | |
| 2970 | + | |
| 2971 | + | |
| 2972 | + | |
| 2973 | + | |
| 2974 | + | |
| 2975 | + | |
| 2976 | + | |
| 2977 | + | |
| 2978 | + | |
| 2979 | + | |
| 2980 | + | |
| 2981 | + | |
| 2982 | + | |
| 2983 | + | |
| 2984 | + | |
| 2985 | + | |
| 2986 | + | |
| 2987 | + | |
| 2988 | + | |
2955 | 2989 | | |
2956 | 2990 | | |
2957 | 2991 | | |
| |||
0 commit comments