Skip to content

Commit 9450ca1

Browse files
author
Petter Moe Kvalvaag
authored
Add possibility for token authentication (#102)
* Add possibility for token authentication * Add documentation for TOKEN setting
1 parent 88fad25 commit 9450ca1

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ in DATABASES control the behavior of the backend:
6767

6868
String. Database user password.
6969

70+
- TOKEN
71+
72+
String. Access token fetched as a user or service principal which
73+
has access to the database. E.g. when using `azure.identity`, the
74+
result of `DefaultAzureCredential().get_token('https://database.windows.net/.default')`
75+
can be passed.
76+
7077
- AUTOCOMMIT
7178

7279
Boolean. Set this to `False` if you want to disable

mssql/base.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import re
99
import time
10+
import struct
1011

1112
from django.core.exceptions import ImproperlyConfigured
1213

@@ -53,7 +54,22 @@ def encode_connection_string(fields):
5354
'%s=%s' % (k, encode_value(v))
5455
for k, v in fields.items()
5556
)
57+
def prepare_token_for_odbc(token):
58+
"""
59+
Will prepare token for passing it to the odbc driver, as it expects
60+
bytes and not a string
61+
:param token:
62+
:return: packed binary byte representation of token string
63+
"""
64+
if not isinstance(token, str):
65+
raise TypeError("Invalid token format provided.")
5666

67+
tokenstr = token.encode()
68+
exptoken = b""
69+
for i in tokenstr:
70+
exptoken += bytes({i})
71+
exptoken += bytes(1)
72+
return struct.pack("=i", len(exptoken)) + exptoken
5773

5874
def encode_value(v):
5975
"""If the value contains a semicolon, or starts with a left curly brace,
@@ -294,7 +310,7 @@ def get_new_connection(self, conn_params):
294310
cstr_parts['UID'] = user
295311
if 'Authentication=ActiveDirectoryInteractive' not in options_extra_params:
296312
cstr_parts['PWD'] = password
297-
else:
313+
elif 'TOKEN' not in conn_params:
298314
if ms_drivers.match(driver) and 'Authentication=ActiveDirectoryMsi' not in options_extra_params:
299315
cstr_parts['Trusted_Connection'] = trusted_connection
300316
else:
@@ -324,11 +340,17 @@ def get_new_connection(self, conn_params):
324340
conn = None
325341
retry_count = 0
326342
need_to_retry = False
343+
args = {
344+
'unicode_results': unicode_results,
345+
'timeout': timeout,
346+
}
347+
if 'TOKEN' in conn_params:
348+
args['attrs_before'] = {
349+
1256: prepare_token_for_odbc(conn_params['TOKEN'])
350+
}
327351
while conn is None:
328352
try:
329-
conn = Database.connect(connstr,
330-
unicode_results=unicode_results,
331-
timeout=timeout)
353+
conn = Database.connect(connstr, **args)
332354
except Exception as e:
333355
for error_number in self._transient_error_numbers:
334356
if error_number in e.args[1]:

0 commit comments

Comments
 (0)