Skip to content

Commit

Permalink
Add support for generic auth headers
Browse files Browse the repository at this point in the history
Add a auth_header attribute that is used verbatim
as Authorization header (i.e. without prefixing `Token `).
We use this to support oauth2 flows with Bearer tokens
(patch for nautobot will come soon).
  • Loading branch information
sklemer1 committed Nov 26, 2023
1 parent f4498c0 commit e9f1df6
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 25 deletions.
19 changes: 17 additions & 2 deletions pynautobot/core/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class Api(object):
:param str url: The base URL to the instance of Nautobot you
wish to connect to.
:param str token: Your Nautobot token.
:param str auth_header: Content of the ``Authorization``-Header, e.g. for Bearer Token.
:param bool,optional threading: Set to True to use threading in ``.all()``
and ``.filter()`` requests.
:param int,optional max_workers: Set the maximum workers for threading in ``.all()``
Expand All @@ -71,12 +72,22 @@ class Api(object):
... token='d6f4e314a5b5fefd164995169f28ae32d987704f'
... )
>>> nb.dcim.devices.all()
or for oauth2 flows
>>> import pynautobot
>>> nb = pynautobot.api(
... 'http://localhost:8000',
... auth_header='Bearer keycloak eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSl...'
... )
>>> nb.dcim.devices.all()
"""

def __init__(
self,
url,
token=None,
auth_header=None,
threading=False,
max_workers=4,
api_version=None,
Expand All @@ -85,7 +96,11 @@ def __init__(
):
base_url = "{}/api".format(url if url[-1] != "/" else url[:-1])
self.token = token
self.headers = {"Authorization": f"Token {self.token}"}
if auth_header:
self.auth_header = auth_header
else:
self.auth_header = f"Token {self.token}"
self.headers = {"Authorization": self.auth_header}
self.base_url = base_url
self.http_session = requests.Session()
self.http_session.verify = verify
Expand Down Expand Up @@ -198,7 +213,7 @@ def status(self):
"""
status = Request(
base=self.base_url,
token=self.token,
auth_header=self.auth_header,
http_session=self.http_session,
api_version=self.api_version,
).get_status()
Expand Down
10 changes: 5 additions & 5 deletions pynautobot/core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def choices(self):

self._choices = Request(
base="{}/{}/_choices/".format(self.api.base_url, self.name),
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
).get()

Expand Down Expand Up @@ -144,7 +144,7 @@ def get_custom_fields(self):
"""
return Request(
base=f"{self.api.base_url}/{self.name}/custom-fields/",
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
).get()

Expand Down Expand Up @@ -176,7 +176,7 @@ def get_custom_field_choices(self):
"""
return Request(
base=f"{self.api.base_url}/{self.name}/custom-field-choices/",
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
).get()

Expand All @@ -201,7 +201,7 @@ def config(self):
self.api.base_url,
self.name,
),
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
).get()
return config
Expand Down Expand Up @@ -241,7 +241,7 @@ def installed_plugins(self):
base="{}/plugins/installed-plugins".format(
self.api.base_url,
),
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
).get()
return installed_plugins
20 changes: 10 additions & 10 deletions pynautobot/core/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def __init__(self, api, app, name, model=None):
self.name = name.replace("_", "-")
self.api = api
self.base_url = api.base_url
self.token = api.token
self.auth_header = api.auth_header
self.url = "{base_url}/{app}/{endpoint}".format(
base_url=self.base_url,
app=app.name,
Expand Down Expand Up @@ -101,7 +101,7 @@ def all(self, api_version=None):
api_version = api_version or self.api.api_version
req = Request(
base="{}/".format(self.url),
token=self.token,
auth_header=self.auth_header,
http_session=self.api.http_session,
threading=self.api.threading,
max_workers=self.api.max_workers,
Expand Down Expand Up @@ -166,7 +166,7 @@ def get(self, *args, **kwargs):
req = Request(
key=key,
base=self.url,
token=self.token,
auth_header=self.auth_header,
http_session=self.api.http_session,
api_version=api_version,
)
Expand Down Expand Up @@ -237,7 +237,7 @@ def filter(self, *args, api_version=None, **kwargs):
req = Request(
filters=kwargs,
base=self.url,
token=self.token,
auth_header=self.auth_header,
http_session=self.api.http_session,
threading=self.api.threading,
api_version=api_version,
Expand Down Expand Up @@ -302,7 +302,7 @@ def create(self, *args, api_version=None, **kwargs):

req = Request(
base=self.url,
token=self.token,
auth_header=self.auth_header,
http_session=self.api.http_session,
api_version=api_version,
).post(args[0] if args else kwargs)
Expand Down Expand Up @@ -338,7 +338,7 @@ def update(self, id: str, data: Dict[str, any]):
req = Request(
key=id,
base=self.url,
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
api_version=self.api.api_version,
)
Expand Down Expand Up @@ -385,7 +385,7 @@ def choices(self, api_version=None):

req = Request(
base=self.url,
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
api_version=api_version,
).options()
Expand Down Expand Up @@ -442,7 +442,7 @@ def count(self, *args, api_version=None, **kwargs):
api_version = api_version or self.api.api_version

ret = Request(
filters=kwargs, base=self.url, token=self.token, http_session=self.api.http_session, api_version=api_version
filters=kwargs, base=self.url, auth_header=self.auth_header, http_session=self.api.http_session, api_version=api_version
)

return ret.get_count()
Expand All @@ -462,7 +462,7 @@ def __init__(self, parent_obj, name, custom_return=None):

self.request_kwargs = dict(
base=self.url,
token=parent_obj.api.token,
auth_header=parent_obj.api.auth_header,
http_session=parent_obj.api.http_session,
)

Expand Down Expand Up @@ -560,7 +560,7 @@ def run(self, *args, api_version=None, **kwargs):

req = Request(
base=job_run_url,
token=self.token,
auth_header=self.auth_header,
http_session=self.api.http_session,
api_version=api_version,
).post(args[0] if args else kwargs)
Expand Down
13 changes: 9 additions & 4 deletions pynautobot/core/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def __init__(
filters=None,
key=None,
token=None,
auth_header=None,
threading=False,
max_workers=4,
api_version=None,
Expand All @@ -152,6 +153,10 @@ def __init__(
self.filters = filters
self.key = key
self.token = token
if auth_header:
self.auth_header = auth_header
else:
self.auth_header = f"Token {self.token}"
self.http_session = http_session
self.url = self.base if not key else "{}{}/".format(self.base, key)
self.threading = threading
Expand Down Expand Up @@ -214,8 +219,8 @@ def get_status(self):
:Raises: RequestError if request is not successful.
"""
headers = {"Content-Type": "application/json;"}
if self.token:
headers["authorization"] = "Token {}".format(self.token)
if self.auth_header:
headers["authorization"] = self.auth_header

if self.api_version:
headers["accept"] = f"application/json; version={self.api_version}"
Expand Down Expand Up @@ -246,8 +251,8 @@ def _make_call(self, verb="get", url_override=None, add_params=None, data=None):
else:
headers = {"accept": "application/json;"}

if self.token:
headers["authorization"] = "Token {}".format(self.token)
if self.auth_header:
headers["authorization"] = self.auth_header

if self.api_version:
headers["accept"] = f"application/json; version={self.api_version}"
Expand Down
6 changes: 3 additions & 3 deletions pynautobot/core/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def full_details(self):
if self.url:
req = Request(
base=self.url,
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
api_version=self.api.api_version,
)
Expand Down Expand Up @@ -385,7 +385,7 @@ def save(self):
req = Request(
key=self.id,
base=self.endpoint.url,
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
api_version=self.api.api_version,
)
Expand Down Expand Up @@ -433,7 +433,7 @@ def delete(self):
req = Request(
key=self.id,
base=self.endpoint.url,
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
api_version=self.api.api_version,
)
Expand Down
2 changes: 1 addition & 1 deletion pynautobot/models/dcim.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def trace(self):
req = Request(
key=str(self.id) + "/trace",
base=self.endpoint.url,
token=self.api.token,
auth_header=self.api.auth_header,
http_session=self.api.http_session,
)
uri_to_obj_class_map = {
Expand Down

0 comments on commit e9f1df6

Please sign in to comment.