Skip to content

Commit 9578f78

Browse files
authored
Merge pull request #140 from atomic-labs/oauth-update
Migrate from username/password auth to oauth
2 parents d706864 + f8a2bf2 commit 9578f78

File tree

3 files changed

+29
-25
lines changed

3 files changed

+29
-25
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Lastly, if the asset_tag field is blank in JAMF when it is being created in Snip
4949

5050
- Python3 (3.7 or higher) is installed on your system with the requests, json, time, and configparser python libs installed.
5151
- Network access to both your JAMF and Snipe-IT environments.
52-
- A JAMF username and password that has read & write permissions for computer assets, mobile device assets, and users.
52+
- A JAMF client_id and client_secret for the JAMF API that has read & write permissions for computer assets, mobile device assets, and users.
5353
- Computers: Read, Update
5454
- Mobile Devices: Read, Update
5555
- Users: Read, Update
@@ -92,8 +92,8 @@ Note: do not add `""` or `''` around any values.
9292
**[jamf]**
9393

9494
- `url`: https://*your_jamf_instance*.com:*port*
95-
- `username`: Jamf API user username
96-
- `password`: Jamf API user password
95+
- `client_id`: Jamf API client ID
96+
- `client_secret`: Jamf API client secret
9797

9898
**[snipe-it]**
9999

jamf2snipe

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,13 @@ try:
134134
jamfpro_base = config['jamf']['url']
135135
logging.debug("The configured Jamf Pro base url is: {}".format(jamfpro_base))
136136

137-
logging.info("Setting the username to request an api key.")
138-
jamf_user = config['jamf']['username']
139-
logging.debug("The user you provided for Jamf is: {}".format(jamf_user))
137+
logging.info("Setting the client_id to request an api key.")
138+
jamf_client_id = config['jamf']['client_id']
139+
logging.debug("The client_id you provided for Jamf is: {}".format(jamf_client_id))
140140

141-
logging.info("Setting the password to request an api key.")
142-
jamf_password = config['jamf']['password']
143-
logging.debug("The password you provided for Jamf is: {}".format(jamf_user))
141+
logging.info("Setting the client_secret to request an api key.")
142+
jamf_client_secret = config['jamf']['client_secret']
143+
logging.debug("The client_secret you provided for Jamf is: {}".format(jamf_client_secret))
144144

145145
# This is the address, cname, or FQDN for your snipe-it instance.
146146
logging.info("Setting the base URL for SnipeIT.")
@@ -213,7 +213,7 @@ retries = Retry(
213213
)
214214
session.mount('https://', HTTPAdapter(max_retries=retries))
215215

216-
# Use Basic Auth to request a Jamf Token.
216+
# Use the client credentials to request a Jamf Token.
217217
def request_jamf_token():
218218
# Tokens expire after 60 minutes, but we can't be sure that we're in the same TZ as the Jamf server, so we'll set up a timer.
219219
global token_request_time
@@ -223,33 +223,37 @@ def request_jamf_token():
223223
global expires_time
224224
token_request_time = time.time()
225225
logging.info("Requesting a new token at {}.".format(token_request_time))
226-
api_url = '{0}/api/v1/auth/token'.format(jamfpro_base)
226+
api_url = '{0}/api/v1/oauth/token'.format(jamfpro_base)
227227
# No hook for this api call.
228228
logging.debug('Calling for a token against: {}\n The username and password can be found earlier in the script.'.format(api_url))
229229
# No hook for this API call.
230-
response = session.post(api_url, auth=(jamf_user, jamf_password), headers=jamfbasicheaders, verify=user_args.do_not_verify_ssl)
230+
data = {
231+
'client_id': jamf_client_id,
232+
'client_secret': jamf_client_secret,
233+
"grant_type": "client_credentials"
234+
}
235+
logging.debug('The data being sent to JamfPro for token request is: {}'.format(data))
236+
response = session.post(api_url, data=data, verify=user_args.do_not_verify_ssl)
231237
if response.status_code == 200:
232238
logging.debug("Got back a valid 200 response code.")
233239
jsonresponse = response.json()
234240
logging.debug(jsonresponse)
235241
# So we have our token and Expires time. Set the expires time globably so we can reset later.
236242
try:
237-
expires_time = datetime.datetime.fromisoformat(jsonresponse['expires'].replace("Z", "+00:00"))
243+
expires_in = int(jsonresponse['expires_in'])
244+
expires_time = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=expires_in)
238245
except:
239-
# APIs are awful and Jamf doesn't always send enough ms digits. UGH.
240-
try:
241-
expires_time = datetime.datetime.fromisoformat(jsonresponse['expires'].replace("Z", "0+00:00"))
242-
except:
243-
logging.error("Jamf sent a malformed timestamp: {}\n Please feel free to complain to Jamf support.".format(jsonresponse['expires']))
244-
raise SystemExit("Unable to grok Jamf Timestamp - Exiting")
245-
logging.debug("Token expires in: {}".format(expires_time - datetime.datetime.now(datetime.timezone.utc)))
246+
logging.error("Jamf sent a malformed timestamp: {}\n Please feel free to complain to Jamf support.".format(jsonresponse['expires_in']))
247+
raise SystemExit("Unable to grok Jamf Timestamp - Exiting")
248+
logging.debug("Token expires in: {}".format(expires_in))
246249
# The headers are also global, because they get used elsewhere.
247250
logging.info("Setting new jamf headers with bearer token")
248-
jamfheaders = {'Authorization': 'Bearer {}'.format(jsonresponse['token']),'Accept': 'application/json','Content-Type':'application/json'}
249-
jamfxmlheaders = {'Authorization': 'Bearer {}'.format(jsonresponse['token']),'Accept': 'application/xml','Content-Type':'application/xml'}
251+
jamfheaders = {'Authorization': 'Bearer {}'.format(jsonresponse['access_token']),'Accept': 'application/json','Content-Type':'application/json'}
252+
jamfxmlheaders = {'Authorization': 'Bearer {}'.format(jsonresponse['access_token']),'Accept': 'application/xml','Content-Type':'application/xml'}
250253
logging.debug('Request headers for JamfPro will be: {}\nRequest headers for Snipe will be: {}'.format(jamfheaders, snipeheaders))
251254
else:
252-
logging.error("Could not obtain a token for use with Jamf's classic API. Please check your username and password.")
255+
logging.error("Could not obtain a token for use with Jamf's classic API. Please check your client_id and client_secret.")
256+
logging.debug('Response code: {} - {}'.format(response.status_code, response.content))
253257
raise SystemExit("Unable to obtain Jamf Token")
254258

255259

settings.conf.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[jamf]
22
# This entire section is Required
33
url = https://yourinstance.jamfcloud.com
4-
username = yourJamfUsername
5-
password = $ecretJ@mfPassw0rd
4+
client_id = yourClientID
5+
client_secret = yourClientSecret
66

77
[snipe-it]
88
#Required

0 commit comments

Comments
 (0)