Skip to content

Commit

Permalink
Cargo Ship 1.0.3, Tugboat 1.5.3
Browse files Browse the repository at this point in the history
Added LDAP login to both, multiprocessing to Cargo Ship
  • Loading branch information
lazymutt committed Jan 4, 2018
1 parent 031f108 commit 3070227
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 223 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ Tugboat is designed to make modifications to specific computer records on your J

| Date | Version | Notes |
| ---------- | ------- | ---------------------------------------- |
| 2018.01.03 | | Updated Tugboat 1.5.3 and Cargo Ship 1.0.3 |
| 2017.04.11 | | Updated Tugboat 1.5.2 and Cargo Ship 1.0.1 |
| 2017.02.15 | | Initial release. |
Binary file added cargo_ship/Cargo Ship application macOS.dmg
Binary file not shown.
Binary file removed cargo_ship/Cargo Ship application.dmg
Binary file not shown.
17 changes: 10 additions & 7 deletions cargo_ship/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ This tool is identical to our inhouse version. However it can be customized to f

I've been getting some feedback from users in environments with 150+ policies that it can take on the order of 8 minutes from login to UI display due to the need to parse each policy individually in order to build the required data structures. I'm taking this feedback in consideration and am looking at ways to possibly postpone this step until requested by the user.

**4/11/17: Released version 1.5.2 of Cargo Ship. Lots of tweaks, added logging with management_tools, much improved login.**

**[Jamf's release notes](http://docs.jamf.com/9.98/casper-suite/release-notes/Bug_Fixes_and_Enhancements.html) for version 9.98 say that PI-003395 has been corrected. We're unable to test this and we're eager to hear from others if this is the case.**
4/11/17: Released version 1.0.1 of Cargo Ship. Lots of tweaks, added logging with management_tools, much improved login.

[Jamf's release notes](http://docs.jamf.com/9.98/casper-suite/release-notes/Bug_Fixes_and_Enhancements.html) for version 9.98 say that PI-003395 has been corrected. We're unable to test this and we're eager to hear from others if this is the case.

**1/3/18: Released version 1.0.3 of Cargo Ship. Improved LDAP logins, added multiprocessing to particularly resource-hungry area of code.**

## Contents

Expand All @@ -41,6 +41,8 @@ If you have any comments, questions, or other input, either [file an issue](../.
## System Requirements

- Python 2.7+ (which you can download [here](https://www.python.org/download/))
- Multiprocess 0.70.5+ (which you can download [here](https://pypi.python.org/pypi/multiprocess))
- Management tools (which you can download [here](https://github.com/univ-of-utah-marriott-library-apple/management_tools/releases))

If you intend to rebuild customized versions you will need the following tools, depending on your platform:

Expand Down Expand Up @@ -144,8 +146,9 @@ My heartfelt thanks to the other members of the Mac Group and the IT administrat

## Update History

| Date | Version | Notes |
| --------- | ------- | ---------------------------------------- |
| 2017.4.11 | 1.1.0 | Logging with management_tools, login and search much improved. Other tweaks. |
| 2017.2.15 | 1.0.0 | Initial public release. |
| Date | Version | Notes |
| ---------- | ------- | ---------------------------------------- |
| 2018.01.03 | 1.0.3 | Improved LDAP logins, multiprocess policy fetching. |
| 2017.04.11 | 1.0.1 | Logging with management_tools, login and search much improved. Other tweaks. |
| 2017.02.15 | 1.0.0 | Initial public release. |

Binary file removed cargo_ship/cargo_ship.ico
Binary file not shown.
403 changes: 279 additions & 124 deletions cargo_ship/cargo_ship.py

Large diffs are not rendered by default.

22 changes: 0 additions & 22 deletions cargo_ship/setup_win.py

This file was deleted.

16 changes: 9 additions & 7 deletions tugboat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ This version is highly modified from our internal version. Please see the follow

2/16/17: Unfortunately it seems a lot of people are running into the LDAP bug I reference in the [blog post](https://apple.lib.utah.edu/?p=2057). If you are attempting to login as an LDAP user, Jamf does not map it's LDAP group permissions correctly. This is a known issue referred to as Product Issue PI-003395 inside Jamf. I urge you to contact your Jamf Technical Representative(s) and elevate this issue with them.

**4/11/17: Released version 1.5.2 of Tugboat. Lots of tweaks, added logging with management_tools, much improved login, searching and top user detection.**

**[Jamf's release notes](http://docs.jamf.com/9.98/casper-suite/release-notes/Bug_Fixes_and_Enhancements.html) for version 9.98 say that PI-003395 has been corrected. We're unable to test this and we're eager to hear from others if this is the case.**
4/11/17: Released version 1.5.2 of Tugboat. Lots of tweaks, added logging with management_tools, much improved login, searching and top user detection.

[Jamf's release notes](http://docs.jamf.com/9.98/casper-suite/release-notes/Bug_Fixes_and_Enhancements.html) for version 9.98 say that PI-003395 has been corrected. We're unable to test this and we're eager to hear from others if this is the case.

**1/3/18: Released version 1.5.3 of Tugboat. Improved LDAP logins.**

## Contents

Expand Down Expand Up @@ -61,6 +61,7 @@ If you have any comments, questions, or other input, either [file an issue](../.

- Python 2.7+ (which you can download [here](https://www.python.org/download/))
- Pexpect 3.3+ (which you can download [here](https://github.com/pexpect/pexpect))
- Management tools (which you can download [here](https://github.com/univ-of-utah-marriott-library-apple/management_tools/releases))

If you intend to rebuild customized versions you will need the following tools, depending on your platform:

Expand Down Expand Up @@ -226,8 +227,9 @@ My heartfelt thanks to the other members of the Mac Group and the IT administrat

## Update History

| Date | Version | Notes |
| --------- | ------- | ---------------------------------------- |
| 2017.4.11 | 1.5.2 | Logging with management_tools, login and search much improved, top user improved. Other tweaks. |
| 2017.2.15 | 1.5.0 | Initial public release. |
| Date | Version | Notes |
| ---------- | ------- | ---------------------------------------- |
| 2018.01.03 | 1.5.3 | Improved LDAP logins. |
| 2017.04.11 | 1.5.2 | Logging with management_tools, login and search much improved, top user improved. Other tweaks. |
| 2017.02.15 | 1.5.0 | Initial public release. |

Binary file added tugboat/Tugboat application macOS.dmg
Binary file not shown.
Binary file removed tugboat/Tugboat application.dmg
Binary file not shown.
22 changes: 0 additions & 22 deletions tugboat/setup_win.py

This file was deleted.

191 changes: 150 additions & 41 deletions tugboat/tugboat.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# 1.5.0 2017.02.15 Initial public release. tjm
# 1.5.2 2017.02.15 Logging with management_tools, login and search
# much improved, top user improved. Other tweaks. tjm
#
# 1.5.3 2018.1.xx Added LDAP login logic. tjm
#
################################################################################

Expand Down Expand Up @@ -342,7 +342,7 @@ def build_ui(self):
BAMCAQAAOw==
'''

self.root.title("Tugboat 1.5.2")
self.root.title("Tugboat 1.5.3")

self.mainframe = ttk.Frame(self.root)

Expand Down Expand Up @@ -1470,72 +1470,181 @@ def try_login():
"""
jamf api call for login test
"""

def call_jss(logger, api_call):
"""
consolidate API calls to single function
pass in logger and api call.
"""
logger.info("%s: activated" % inspect.stack()[0][3])

try:
url = jamf_hostname.get() + '/JSSResource/' + api_call

logger.info("%s called with %s" % (inspect.stack()[0][3], api_call))

request = urllib2.Request(url)
request.add_header('Accept', 'application/json')
request.add_header('Authorization', 'Basic ' + base64.b64encode(jamf_username.get() + ':' + jamf_password.get()))

response = urllib2.urlopen(request)

logger.info("Code returned: %s %s" % (response.code, api_call))

if response.code != 200:
logger.error("login: Invalid response from Jamf (" + api_call + ")")
tkMessageBox.showerror("Jamf login", "Invalid response from Jamf")
root.destroy() # clean up after yourself!
sys.exit()

response_json = json.loads(response.read())

return response_json

#
# handle various communication errors
except urllib2.HTTPError, error:

logger.error("Code returned: %s %s" % (response.code, api_call))

if error.code == 401:
logger.error("%s: Invalid username or password. (%r) (%s)" % (inspect.stack()[0][3], jamf_username.get(), api_call))
tkMessageBox.showerror("Jamf login", "Invalid username or password.")
else:
logger.error("%s: Error communicating with JSS. %s %s" % (inspect.stack()[0][3], jamf_hostname.get(), api_call))
tkMessageBox.showerror("Jamf login", "HTTP error from:\n%s" % jamf_hostname.get())
except urllib2.URLError:
logger.error("%s: Error contacting JSS: %s %s" % (inspect.stack()[0][3], jamf_hostname.get(), api_call))
tkMessageBox.showerror("Jamf login", "Unable to contact:\n%s" % jamf_hostname.get())
except Exception as exception_message:
logger.error("%s: Generic error. (%r) %s" % (inspect.stack()[0][3], exception_message, api_call))
tkMessageBox.showerror("Jamf login", "Generic error from %s." % jamf_hostname.get())

#
# handle bad condition exits here...
logger.error("%s: Exiting, Error calling %s" % (inspect.stack()[0][3], api_call))
sys.exit()

logger.info("%s: activated" % inspect.stack()[0][3])
try:
url = jamf_hostname.get() + '/JSSResource/accounts/username/' + jamf_username.get()
request = urllib2.Request(url)
request.add_header('Accept', 'application/json')
request.add_header('Authorization', 'Basic ' + base64.b64encode(jamf_username.get() + ':' + jamf_password.get()))

response = urllib2.urlopen(request)
try:
# Attempt to verify LDAP users.
# 1. Pull JSS acounts for users and groups
# 2. Pull LDAP servers
# 3. Build list of valid groups, those with appropriate rights
# 4. Check each LDAP server for valid groups the user is a member of, login if found
# 5. Fall back to jss user login, check account for appropriate rights

if response.code != 200:
logger.error("login: Invalid response from Jamf")
tkMessageBox.showerror("Jamf login", "Invalid response from Jamf")
root.destroy() # clean up after yourself!
sys.exit()
jss_accounts = call_jss(logger, 'accounts')

response_json = json.loads(response.read())
raw_ldap = call_jss(logger, 'ldapservers')
ldap_servers = raw_ldap['ldap_servers']
logger.info("JSS LDAP servers: %r" % ldap_servers)

#
# store list of user privileges
user_privileges = response_json['account']['privileges']['jss_objects']
# store list of user and group privileges
user_list = jss_accounts['accounts']['users']
group_list = jss_accounts['accounts']['groups']

#
# stop number of require privileges
count_privileges = len(required_privileges)
# find groups on jss that have required_privileges
valid_groups = []
for item in group_list:
raw_privs = call_jss(logger, 'accounts/groupid/' + str(item['id']))
this_group_privs = raw_privs['group']['privileges']['jss_objects']

count_privileges = len(required_privileges)

for this_privilege in required_privileges:
if this_privilege in this_group_privs:
count_privileges -= 1

if count_privileges == 0:
logger.info("%s is valid." % item['name'])
valid_groups.append([item['id'], item['name']])

#
# for every required privilege
# check if it's in user privileges
# decrement if yes
# maintain list of missing require privileges
missing_privileges = []

for item in required_privileges:
if item in user_privileges:
count_privileges -= 1
else:
missing_privileges.append(item)
# find servers with valid groups the user is a member of
valid_servers = []
for server in ldap_servers:
for group in valid_groups:
raw_group_membership = call_jss(logger, 'ldapservers/id/' + str(server['id']) + '/group/' + urllib.quote(str(group[1])) + '/user/' + jamf_username.get())

if raw_group_membership['ldap_users']:
valid_servers.append(server['name'])

#
# if all require privileges accounted for, proceed
# else alert and fail
if count_privileges == 0:
logger.info("login: valid login. (%r)" % jamf_username.get())
# if we found a valid server, proceed to app
if valid_servers:
logger.info('valid server found, login successful.')
root.destroy() # clean up after yourself!
return

#
# check users account privileges for required rights
else:
logger.error("login: User %r lacks appropriate privileges: %r" % (jamf_username.get(), missing_privileges))
tkMessageBox.showerror("Jamf login", "User lacks appropriate privileges.")
raw_privileges = call_jss(logger, 'accounts/username/' + jamf_username.get())
user_privileges = raw_privileges['account']['privileges']['jss_objects']

except:
logger.error("login: Invalid username or password. (%r)" % jamf_username.get())
tkMessageBox.showerror("Jamf login", "Invalid username or password.")
sys.exit()
#
# stop number of require privileges
count_privileges = len(required_privileges)

#
# for every required privilege
# check if it's in user privileges
# decrement if yes
# maintain list of missing require privileges
missing_privileges = []

for item in required_privileges:
if item in user_privileges:
count_privileges -= 1
else:
missing_privileges.append(item)

#
# if all require privileges accounted for, proceed
# else alert and fail
if count_privileges == 0:
logger.info("login: valid login. (%r)" % jamf_username.get())
root.destroy() # clean up after yourself!
return
else:
logger.error("login: User %r lacks appropriate privileges: %r" % (jamf_username.get(), missing_privileges))
tkMessageBox.showerror("Jamf login", "User lacks appropriate privileges.\n%r" % missing_privileges)

#
# handle various communication errors
except urllib2.HTTPError, error:

logger.info("Code returned: %s" % error.code)

if error.code == 401:
logger.error("%s: Invalid username or password. (%r)" % (inspect.stack()[0][3], jamf_username.get()))
tkMessageBox.showerror("Jamf login", "Invalid username or password.")
else:
logger.error("%s: Error communicating with JSS. %s" % (inspect.stack()[0][3], jamf_hostname.get()))
tkMessageBox.showerror("Jamf login", "HTTP error from:\n%s" % jamf_hostname.get())
except urllib2.URLError:
logger.error("%s: Error contacting JSS: %s" % (inspect.stack()[0][3], jamf_hostname.get()))
tkMessageBox.showerror("Jamf login", "Unable to contact:\n%s" % jamf_hostname.get())
except Exception as exception_message:
logger.error("%s: Generic error. (%r)" % (inspect.stack()[0][3], exception_message))
tkMessageBox.showerror("Jamf login", "Generic error from %s." % jamf_hostname.get())

sys.exit()

#
# This is really important. This list contains the required rights.
# This is really important. This list contains the required rights for the fields we need to access.
required_privileges = ['Read Accounts', 'Read Buildings', 'Read Computers', 'Update Computers', 'Read Departments', 'Read User', 'Update User']

root = Tk()
jamf_username = StringVar()
jamf_password = StringVar()
jamf_hostname = StringVar()

# customizable for specific deployments
# customizable for specific deployment
jamf_hostname.set("https://your.jamf.server:8443")

#
Expand Down
Binary file removed tugboat/tugboat_icon.ico
Binary file not shown.

0 comments on commit 3070227

Please sign in to comment.