Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions .github/workflows/main_pr_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@ jobs:
- name: Tests
run: make test

exp-integration-tests:
continue-on-error: true
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
cache: 'pip'

- name: Install
run: make install

- name: Run Integration Tests
run: make test-all
env:
JAMF_PRO_HOST: ${{ vars.JAMF_PRO_HOST }}
JAMF_PRO_CLIENT_ID: ${{ vars.JAMF_PRO_CLIENT_ID }}
JAMF_PRO_CLIENT_SECRET: ${{ vars.JAMF_PRO_CLIENT_SECRET }}
# exp-integration-tests:
# continue-on-error: true
# runs-on: ubuntu-latest
# steps:
# - name: Checkout
# uses: actions/checkout@v3

# - name: Setup Python
# uses: actions/setup-python@v4
# with:
# python-version: '3.9'
# cache: 'pip'

# - name: Install
# run: make install

# - name: Run Integration Tests
# run: make test-all
# env:
# JAMF_PRO_HOST: ${{ vars.JAMF_PRO_HOST }}
# JAMF_PRO_CLIENT_ID: ${{ vars.JAMF_PRO_CLIENT_ID }}
# JAMF_PRO_CLIENT_SECRET: ${{ vars.JAMF_PRO_CLIENT_SECRET }}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
A client library for the Jamf Pro APIs and webhooks.

```python
from jamf_pro_sdk import JamfProClient, BasicAuthProvider
from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider

client = JamfProClient(
server="dummy.jamfcloud.com",
credentials=BasicAuthProvider("username", "password")
credentials=ApiClientCredentialsProvider("client_id", "client_secret")
)

all_computers = client.pro_api.get_computer_inventory_v1()
Expand Down
39 changes: 26 additions & 13 deletions docs/reference/credentials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,43 @@
Credentials Providers
=====================

API Client Providers
--------------------
The Jamf Pro SDK has two primary types of credential providers: **API Client Credentials** and **User Credentials**.

These credentials providers use Jamf Pro API clients for API authentication.
API Client Credentials Provider
-------------------------------

Use Jamf Pro `API clients <https://developer.jamf.com/jamf-pro/docs/client-credentials>`_ for API authentication.

.. autoclass:: jamf_pro_sdk.clients.auth.ApiClientCredentialsProvider
:members:

Basic Auth Providers
--------------------
User Credentials Provider
-------------------------

These credentials providers use a username and password for API authentication.
User credential providers use a username and password for API authentication.

.. autoclass:: jamf_pro_sdk.clients.auth.BasicAuthProvider
.. autoclass:: jamf_pro_sdk.clients.auth.UserCredentialsProvider
:members:

.. autoclass:: jamf_pro_sdk.clients.auth.PromptForCredentials
:members:
Utilities for Credential Providers
----------------------------------

.. autoclass:: jamf_pro_sdk.clients.auth.LoadFromKeychain
:members:
These functions return an instantiated credentials provider of the specified type.

.. autoclass:: jamf_pro_sdk.clients.auth.LoadFromAwsSecretsManager
:members:
Prompt for Credentials
^^^^^^^^^^^^^^^^^^^^^^

.. autofunction:: jamf_pro_sdk.clients.auth.prompt_for_credentials

Load from AWS Secrets Manager
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. autofunction:: jamf_pro_sdk.clients.auth.load_from_aws_secrets_manager

Load from Keychain
^^^^^^^^^^^^^^^^^^

.. autofunction:: jamf_pro_sdk.clients.auth.load_from_keychain

Access Token
------------
Expand Down
10 changes: 8 additions & 2 deletions docs/user/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ A :class:`~jamf_pro_sdk.clients.auth.CredentialsProvider` is an interface for th

The following example does not accept a username or password and retrieves a token from a DynamoDB table in an AWS account (it is assumed an external process is managing this table entry).

.. code-block:: python

>>> import boto3
>>> from jamf_pro_sdk.clients.auth import CredentialsProvider
>>> from jamf_pro_sdk.models.client import AccessToken
Expand Down Expand Up @@ -35,13 +37,17 @@ The SDK's clients provide curated methods to a large number of Jamf Pro APIs. No

Here is the built-in method for getting a computer from the Classic API:

.. code-block:: python

>>> computer = client.classic_api.get_computer_by_id(1)
>>> type(computer)
<class 'jamf_pro_sdk.models.classic.computers.Computer'>
>>>

The same operation can be performed by using the :meth:`~jamf_pro_sdk.clients.JamfProClient.classic_api_request` method directly:

.. code-block:: python

>>> response = client.classic_api_request(method='get', resource_path='computers/id/1')
>>> type(response)
<class 'requests.models.Response'>
Expand All @@ -59,12 +65,12 @@ Here is a code example using :meth:`~jamf_pro_sdk.clients.JamfProClient.concurre

.. code-block:: python

from jamf_pro_sdk import JamfProClient, BasicAuthProvider
from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider

# The default concurrency setting is 10.
client = JamfProClient(
server="jamf.my.org",
credentials=BasicAuthProvider("oscar", "j@mf1234!")
credentials=ApiClientCredentialsProvider("client_id", "client_secret")
)

# Get a list of all computers, and then their IDs.
Expand Down
4 changes: 2 additions & 2 deletions docs/user/classic_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ Assume this client has been instantiated for the examples shown below.

.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, BasicAuthProvider
>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider
>>> client = JamfProClient(
... server="jamf.my.org",
... credentials=BasicAuthProvider("oscar", "j@mf1234!")
... credentials=ApiClientCredentialsProvider("client_id", "client_secret")
... )
>>>

Expand Down
127 changes: 114 additions & 13 deletions docs/user/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,128 @@ When running ``pip freeze`` the SDK will appear with a filepath to the source in
Create a Client
---------------

Import the Jamf Pro client from the SDK:
Create a client object using an API Client ID and Client Secret - the **recommended** method for authentication:

>>> from jamf_pro_sdk import JamfProClient, BasicAuthProvider
.. important::

**Breaking Change**: As of version ``0.8a1``, the SDK no longer uses ``BasicAuthProvider`` objects. Use :class:`~jamf_pro_sdk.clients.auth.ApiClientCredentialsProvider` as the new default.

Create a client object passing in your Jamf Pro server name and a username and password:
`Basic authentication is now disabled by default <https://developer.jamf.com/jamf-pro/docs/classic-api-authentication-changes#basic-authentication>`_ in Jamf Pro. To authenticate securely and ensure compatibility with future Jamf Pro versions, use an API Client for access tokens instead.

.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider
>>> client = JamfProClient(
... server="jamf.my.org",
... credentials=ApiClientCredentialsProvider("client_id", "client_secret")
... )
>>>

.. _server_scheme:

.. note::

When passing your Jamf Pro server name, do not include the scheme (``https://``) as the SDK handles this automatically for you.

Choosing a Credential Provider
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

There are a number of built-in :doc:`/reference/credentials` available. To learn how to implement your own visit :ref:`user/advanced:Custom Credentials Providers`.

**We recommend using API Clients** for most cases. Basic authentication via username and password is now considered a legacy method and is **disabled by default** in Jamf Pro versions ≥ 10.49.

- Use :class:`~jamf_pro_sdk.clients.auth.ApiClientCredentialsProvider` for API Clients.
- Use :class:`~jamf_pro_sdk.clients.auth.UserCredentialsProvider` if enabled in your Jamf environment.

.. important::

**Do not use plaintext secrets (passwords, clients secrets, etc.) in scripts or the console.** The use of the base ``UserCredentialsProvider`` class in this guide is for demonstration purposes.

Credential Provider Utility Functions
-------------------------------------

The SDK contains three helper functions that will *return* an instantiated credential provider of the specified type. When leveraging these functions, ensure you have the required extra dependencies installed.

Prompting for Credentials
^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider, prompt_for_credentials
>>> client = JamfProClient(
... server="jamf.my.org",
... credentials=BasicAuthProvider("oscar", "j@mf1234!")
... credentials=prompt_for_credentials(
... provider_type=ApiClientCredentialsProvider
... )
... )
>>>
API Client ID: 123456abcdef
API Client Secret:

The ``BasicAuthProvider`` is a credentials provider. These objects are interfaces for authenticating for access tokens to the Jamf Pro APIs. Basic auth credentials providers use a username and password for authentication when requesting a new token.
Loading from AWS Secrets Manager
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To use an API Client for authentication (`Jamf Pro 10.49+ <https://learn.jamf.com/bundle/jamf-pro-documentation-current/page/API_Roles_and_Clients.html>`_) use :class:`~jamf_pro_sdk.clients.auth.ApiClientCredentialsProvider`.
.. important::

There are a number of built-in :doc:`/reference/credentials` available. To learn how to implement your own visit :ref:`user/advanced:Custom Credentials Providers`.
The ``aws`` dependency is required for this function and can be installed with ``% python3 -m pip install 'jamf-pro-sdk[aws]'``.

The ``SecretString`` is expected to be a JSON string in the following format:

.. code-block:: json

// For UserCredentialsProvider:
{
"username": "oscar",
"password": "******"
}

// For ApiClientCredentialsProvider:
{
"client_id": "abc123",
"client_secret": "xyz456"
}

.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider, load_from_aws_secrets_manager
>>> client = JamfProClient(
... server="jamf.my.org",
... credentials=load_from_aws_secrets_manager(
... provider_type=ApiClientCredentialsProvider,
... secret_id="arn:aws:secretsmanager:us-west-2:111122223333:secret:aes128-1a2b3c"
... )
... )

Loading from Keychain
^^^^^^^^^^^^^^^^^^^^^

.. important::

**Do not plaintext secrets (passwords, clients secrets, etc.) in scripts or the console.** The use of the base ``BasicAuthProvider`` class in this guide is for demonstration purposes.
This utility requires the ``keyring`` extra dependency, which can be installed via ``% python3 -m pip install 'jamf-pro-sdk[macOS]'``.

When using :class:`~jamf_pro_sdk.clients.auth.ApiClientCredentialsProvider`, the SDK expects the client ID and client secret to be stored using the format ``CLIENT_ID`` and ``CLIENT_SECRET`` respectively. For :class:`~jamf_pro_sdk.clients.auth.UserCredentialsProvider`, you will be prompted for a username.

Additionally, the :ref:`server scheme <server_scheme>` does not need to be passed to the ``server`` argument, as the SDK handles this for you.

On the first request made the client will retrieve and cache an access token. This token will be used for all requests up until it nears expiration. At that point the client will refresh the token. If the token has expired the client will basic auth for a new one.
.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider, load_from_keychain
>>> client = JamfProClient(
... server="jamf.my.org",
... credentials=load_from_keychain(
... provider_type=ApiClientCredentialsProvider,
... server="jamf.my.org"
... )
... )

Access Tokens
-------------

On the first request made the client will retrieve and cache an access token. This token will be used for all requests up until it nears expiration. At that point the client will refresh the token. If the token has expired, the client will use the configured credentials provider to request a new one.

You can retrieve the current token at any time:

.. code-block:: python

>>> access_token = client.get_access_token()
>>> access_token
AccessToken(type='user', token='eyJhbGciOiJIUzI1NiJ9...', expires=datetime.datetime(2023, 8, 21, 16, 57, 1, 113000, tzinfo=datetime.timezone.utc), scope=None)
Expand All @@ -71,6 +162,8 @@ You can retrieve the current token at any time:

Both the Classic and Pro APIs are exposed through two interfaces:

.. code-block:: python

>>> client.classic_api
<jamf_pro_sdk.clients.classic_api.ClassicApi object at 0x10503d240>
>>> client.pro_api
Expand All @@ -97,22 +190,26 @@ Some aspects of the Jamf Pro client can be configured at instantiation. These in

The Jamf Pro client will create a default configuration if one is not provided.

>>> from jamf_pro_sdk import JamfProClient, BasicAuthProvider, SessionConfig
.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider, SessionConfig
>>> config = SessionConfig()
>>> config
SessionConfig(timeout=None, max_retries=0, max_concurrency=5, verify=True, cookie=None, ca_cert_bundle=None, scheme='https')
>>>

Here are two examples on how to use a ``SessionConfig`` with the client to disable TLS verification and set a 30 second timeout:

.. code-block:: python

>>> config = SessionConfig()
>>> config.verify = False
>>> config.timeout = 30
>>> config
SessionConfig(timeout=30, max_retries=0, max_concurrency=5, verify=False, cookie=None, ca_cert_bundle=None, scheme='https')
>>> client = JamfProClient(
... server="jamf.my.org",
... credentials=BasicAuthProvider("oscar", "j@mf1234!")
... credentials=ApiClientCredentialsProvider("client_id", "client_secret"),
... session_config=config,
... )
>>>
Expand All @@ -122,7 +219,7 @@ Here are two examples on how to use a ``SessionConfig`` with the client to disab
SessionConfig(timeout=30, max_retries=0, max_concurrency=5, verify=False, cookie=None, ca_cert_bundle=None, scheme='https')
>>> client = JamfProClient(
... server="jamf.my.org",
... credentials=BasicAuthProvider("oscar", "j@mf1234!")
... credentials=ApiClientCredentialsProvider("client_id", "client_secret"),
... session_config=config,
... )
>>>
Expand All @@ -136,6 +233,8 @@ Logging

You can quickly setup console logging using the provided :func:`~jamf_pro_sdk.helpers.logger_quick_setup` function.

.. code-block:: python

>>> import logging
>>> from jamf_pro_sdk.helpers import logger_quick_setup
>>> logger_quick_setup(level=logging.DEBUG)
Expand All @@ -144,5 +243,7 @@ When set to ``DEBUG`` the stream handler and level will also be applied to ``url

If you require different handlers or formatting you may configure the SDK's logger manually.

.. code-block:: python

>>> import logging
>>> sdk_logger = logging.getLogger("jamf_pro_sdk")
8 changes: 4 additions & 4 deletions docs/user/jcds2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ The file upload operation performs multiple API requests in sequence.

.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, BasicAuthProvider
>>> client = JamfProClient("dummy.jamfcloud.com", BasicAuthProvider("demo", "tryitout"))
>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider
>>> client = JamfProClient("dummy.jamfcloud.com", ApiClientCredentialsProvider("client_id", "client_secret"))
>>> client.jcds2.upload_file(file_path="/path/to/my.pkg")
>>>

Expand All @@ -70,8 +70,8 @@ File downloads will retrieve the download URL to the requested JCDS file and the

.. code-block:: python

>>> from jamf_pro_sdk import JamfProClient, BasicAuthProvider
>>> client = JamfProClient("dummy.jamfcloud.com", BasicAuthProvider("demo", "tryitout"))
>>> from jamf_pro_sdk import JamfProClient, ApiClientCredentialsProvider
>>> client = JamfProClient("dummy.jamfcloud.com", ApiClientCredentialsProvider("client_id", "client_secret"))
>>> client.jcds2.download_file(file_name="/path/to/my.pkg", download_path="/path/to/downloads/")
>>>

Expand Down
Loading