macOSLAPS UI

m_donovan
Contributor III

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()
0 REPLIES 0