Posted on 10-01-2018 08:35 AM
First off I am trying to teach myself python so this is most likely not very pythonic. I have been working on implementing Jushua Miller's swift binary macOSLAPS. I also found his python script to access the AD password via the command line. I have used that as well as informaiton from Bryson Tyrrell's PSU MacAdmins GUI session to add a UI for techs to access the password currently as a Self Service script. The problem I am running into is that I would like it to pull the users credentials from their keychain rather than have them log into the "app". Is this possible in python? Here is the script as it is now. Any critques or suggestions would be appreciated.
#!/usr/bin/python
'''Get LAPS password for AD Computers'''
# #################################################################
# This script will allow an admin user with
# the proper domain crednetials to get a LAPS
# password from Active Directory.
# ##################################################################
# Original script by barteardon
# https://github.com/bartreardon/macscripts/blob/master/lapssearch
# Updated script using pyObjC:
# Joshua D. Miller - josh@psu.edu
# The Pennsylvania State University - September 18, 2017
# Updated script for GUI:
# Mike Donovan mike.donovan@killeenisd.org - Killeen ISD - September 28, 2018
# Original UI elements from Bryson Tyrrell's "Craft your own GUI's with Python and Tkinter"
# PSU MacAdmins 2016
# #################################################################
import Tkinter as tk
import AppKit
import datetime
from getpass import getpass
from os import system
from OpenDirectory import (ODSession, ODNode,
kODRecordTypeComputers)
from SystemConfiguration import (SCDynamicStoreCreate,
SCDynamicStoreCopyValue)
def macOSLAPS_Utility(choice, ad_user_name, ad_password, computer_name):
'''Function to connect and pull information from Active Directory
some code borrowed from AD PassMon - Thanks @macmuleblog'''
# Active Directory Connection and Extraction of Data
try:
# Create Net Config
net_config = SCDynamicStoreCreate(None, "net", None, None)
# Get Active Directory Info
ad_info = dict(
SCDynamicStoreCopyValue(
net_config, 'com.apple.opendirectoryd.ActiveDirectory'))
# Create Active Directory Path
adpath = '{0:}/{1:}'.format(ad_info['NodeName'],
ad_info['DomainNameDns'])
# Use Open Directory To Connect to Active Directory
node, error = ODNode.nodeWithSession_name_error_(
ODSession.defaultSession(), adpath, None)
node.setCredentialsWithRecordType_recordName_password_error_(
None, ad_user_name, ad_password, None)
# Grab the Computer Record
computer_record, error = node.
recordWithRecordType_name_attributes_error_(
kODRecordTypeComputers,
"{0:}$".format(computer_name), None, None)
# Convert to Readable Values
values, error = computer_record.
recordDetailsForAttributes_error_(None, None)
# Get LAPS Password for machine
if choice == "1":
cmpPW = values['dsAttrTypeNative:ms-Mcs-AdmPwd'][0]
cmpPWExp = values['dsAttrTypeNative:ms-Mcs-AdmPwdExpirationTime'][0]
#print(cmpPWExp)
dateExp = convertTime(cmpPWExp)
return [cmpPW, dateExp]
elif choice == "2":
computer_record.setValue_forAttribute_error_(
'126227988000000000',
'dsAttrTypeNative:ms-Mcs-AdmPwdExpirationTime',
None)
popupAlert("
Force Expire time Set. Keep in mind that
macOSLAPS will need to run on the system
before the password is changed.")
except StandardError as error:
popupAlert(error)
def popupAlert(msg):
popup = tk.Tk()
popup.wm_title("Get LAPS")
laps = tk.Label(popup, text=msg, font='System 14 bold')
laps.pack(side="top", fill="x", pady=10)
B1 = tk.Button(popup, text="Okay", command = popup.destroy)
B1.pack()
popup.geometry("350x150")
popup.mainloop()
def convertTime(winTime):
# This portion from a larger script from:
# Author: Joakim Svendsen, "joakimbs" using Google's mail services.
# Copyright (c) 2013. Svendsen Tech. All rights reserved.
# BSD 3-clause license.
#print(winTime)
seconds = int(winTime) / 10000000
epoch = seconds - 11644473600
dt = datetime.datetime(2000, 1, 1, 0, 0, 0)
expDate = dt.fromtimestamp(epoch)
return expDate
#/////////////////First UI for credentials
class App(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.pack()
self.master.title("")
self.master.resizable(False, False)
self.master.tk_setPalette(background='#ececec')
self.master.protocol('WM_DELETE_WINDOW', self.click_cancel)
self.master.bind('<Return>', self.click_ok)
self.master.bind('<Escape>', self.click_cancel)
x = (self.master.winfo_screenwidth() - self.master.winfo_reqwidth()) / 2
y = (self.master.winfo_screenheight() - self.master.winfo_reqheight()) / 3
self.master.geometry("+{}+{}".format(x, y))
self.master.config(menu=tk.Menu(self))
tk.Message(self, text="Please authenticate with your username and password.",
font='System 14 bold', justify='left', aspect=800).pack(pady=(15, 0))
dialog_frame = tk.Frame(self)
dialog_frame.pack(padx=20, pady=15, anchor='w')
tk.Label(dialog_frame, text='Username:').grid(row=0, column=0, sticky='w')
self.user_input = tk.Entry(dialog_frame, background='white', width=24)
self.user_input.grid(row=0, column=1, sticky='w')
self.user_input.focus_set()
tk.Label(dialog_frame, text='Password:').grid(row=1, column=0, sticky='w')
self.pass_input = tk.Entry(dialog_frame, background='white', width=24, show='*')
self.pass_input.grid(row=1, column=1, sticky='w')
button_frame = tk.Frame(self)
button_frame.pack(padx=15, pady=(0, 15), anchor='e')
tk.Button(button_frame, text='OK', height=1, width=6, default='active', command=self.click_ok).pack(side='right')
tk.Button(button_frame, text='Cancel', height=1, width=6, command=self.click_cancel).pack(side='right', padx=10)
def click_ok(self, event=None):
#print("The user clicked 'OK':
Username: {}
Password: {}".format(self.user_input.get(), self.pass_input.get()))
global ad_user_name
global ad_password
ad_user_name = self.user_input.get()
ad_password = self.pass_input.get()
if ad_user_name == "" or ad_password == "":
# got an empty string
alertMsg = "Missing Credentials"
popupAlert(alertMsg)
else:
# got a non-empty string
self.master.destroy()
root2 = tk.Tk()
app2 = lapsUI(root2)
AppKit.NSApplication.sharedApplication().activateIgnoringOtherApps_(True)
app2.mainloop()
def click_cancel(self, event=None):
print("The user clicked 'Cancel'")
self.master.destroy()
#/////////////////Second UI for input
class lapsUI(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.pack()
self.master.title("macOSLAPS UI")
self.master.resizable(False, False)
self.master.tk_setPalette(background='#ececec')
self.master.protocol('WM_DELETE_WINDOW', self.click_exit)
#self.master.bind('<Return>', self.click_ok)
self.master.bind('<Escape>', self.click_exit)
x = (self.master.winfo_screenwidth() - self.master.winfo_reqwidth()) / 2
y = (self.master.winfo_screenheight() - self.master.winfo_reqheight()) / 3
self.master.geometry("+{}+{}".format(x, y))
self.master.config(menu=tk.Menu(self))
dialog_frame = tk.Frame(self)
dialog_frame.pack(padx=20, pady=15, anchor='w')
tk.Label(dialog_frame, text='ComputerName').grid(row=0, column=0, sticky='w')
self.cmp_input = tk.Entry(dialog_frame, background='white', width=24)
self.cmp_input.grid(row=1, column=0, sticky='w')
self.cmp_input.focus_set()
tk.Button(dialog_frame, text='Search', height=2, width=6, command=self.click_search).grid(row=1, column=1, sticky='w')
tk.Label(dialog_frame, text='Password').grid(row=3, column=0, sticky='w')
self.pwd_display = tk.Entry(dialog_frame, background='#ececec', width=24)
self.pwd_display.grid(row=4, column=0, sticky='w')
tk.Label(dialog_frame, text='Password Expires').grid(row=6, column=0, sticky='w')
self.exp_display = tk.Entry(dialog_frame, background='#ececec', width=24)
self.exp_display.grid(row=7, column=0, sticky='w')
tk.Button(dialog_frame, text='Expire', height=1, width=6, command=self.click_expire).grid(row=7, column=1, sticky='w')
button_frame = tk.Frame(self)
button_frame.pack(padx=20, pady=(0, 15), anchor='e')
tk.Button(button_frame, text='Exit', height=2, width=6, command=self.click_exit).grid(row=0, column=1, sticky='w')
def click_search(self, event=None):
#print("The user clicked 'Retrieve':
Computer: {}".format(self.cmp_input.get()))
computer_name = self.cmp_input.get()
if computer_name == "":
alertMsg = "Missing ComputerName"
popupAlert(alertMsg)
else:
choice = "1"
result = macOSLAPS_Utility(choice, ad_user_name, ad_password, computer_name)
self.pwd_display.delete(0, tk.END)
self.pwd_display.insert(0, result[0])
#self.pwd_display.configure(state='disabled')
self.exp_display.delete(0, tk.END)
self.exp_display.insert(0, result[1])
#self.exp_display.configure(state='disabled')
def click_expire(self, event=None):
#print("User clicked Reset")
computer_name = self.cmp_input.get()
if computer_name == "":
alertMsg = "Missing ComputerName"
popupAlert(alertMsg)
else:
choice = "2"
macOSLAPS_Utility(choice, ad_user_name, ad_password, computer_name)
def click_exit(self, event=None):
#print("The user clicked 'Exit'")
self.master.destroy()
if __name__ == '__main__':
info = AppKit.NSBundle.mainBundle().infoDictionary()
info['LSUIElement'] = True
root = tk.Tk()
app = App(root)
AppKit.NSApplication.sharedApplication().activateIgnoringOtherApps_(True)
app.mainloop()