Providers/Scripts/2.4x-2.5x/Scripts/nxUser.py (426 lines of code) (raw):
#!/usr/bin/env python
# ===================================
# Copyright (c) Microsoft Corporation. All rights reserved.
# See license.txt for license information.
# ===================================
import os
import sys
import datetime
import imp
import copy
import fnmatch
protocol = imp.load_source('protocol', '../protocol.py')
nxDSCLog = imp.load_source('nxDSCLog', '../nxDSCLog.py')
helperlib = imp.load_source('helperlib', '../helperlib.py')
LG = nxDSCLog.DSCLog
# [ClassVersion("1.0.0"), FriendlyName("nxUser"),SupportsInventory()]
# class MSFT_nxUserResource : OMI_BaseResource
# {
#        [Key, InventoryFilter] string UserName;
#        [write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string Ensure;
#        [write, InventoryFilter] string FullName;
#        [write, InventoryFilter] string Description;
#        [write] string Password;
#        [write] boolean Disabled;
#        [write] boolean PasswordChangeRequired;
#        [write] string HomeDirectory;
#        [write] string GroupID;
#        [read] string UserID;
# };
global show_mof
show_mof = False
def init_vars(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    if UserName is not None:
        UserName = UserName.encode('ascii', 'ignore')
    else:
        UserName = ''
    if Ensure is not None and Ensure != '':
        Ensure = Ensure.encode('ascii', 'ignore').lower()
    else:
        Ensure = 'present'
    if FullName is not None:
        FullName = FullName.encode('ascii', 'ignore')
    else:
        FullName = ''
    if Description is not None:
        Description = Description.encode('ascii', 'ignore')
    else:
        Description = ''
    if Password is not None:
        Password = Password.encode('ascii', 'ignore')
    else:
        Password = ''
    if Disabled is None:
        Disabled = False
    Disabled = ( Disabled == True ) # this arrives as a 0 or 1
    if PasswordChangeRequired is None:
        PasswordChangeRequired = False
    PasswordChangeRequired = ( PasswordChangeRequired == True ) # this arrives as a 0 or 1
    if HomeDirectory is not None:
        HomeDirectory = HomeDirectory.encode('ascii', 'ignore')
    else:
        HomeDirectory = ''
    if GroupID is not None:
        GroupID = GroupID.encode('ascii', 'ignore')
    else:
        GroupID = ''
    return UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID
def Set_Marshall(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    if helperlib.CONFIG_SYSCONFDIR_DSC == "omsconfig":
        return [-1]
    (UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID) = \
        init_vars(UserName, Ensure, FullName, Description, Password,
                  Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    retval = Set(UserName, Ensure, FullName, Description, Password,
                 Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    return retval
def Test_Marshall(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    if helperlib.CONFIG_SYSCONFDIR_DSC == "omsconfig":
        return [-1]
    (UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID) = \
        init_vars(UserName, Ensure, FullName, Description, Password,
                  Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    retval = Test(UserName, Ensure, FullName, Description, Password,
                  Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    return retval
def Get_Marshall(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    if helperlib.CONFIG_SYSCONFDIR_DSC == "omsconfig":
        return [-1]
    arg_names = list(locals().keys())
    (UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID) = \
        init_vars(UserName, Ensure, FullName, Description, Password,
                  Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    retval = 0
    (retval, UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID, UserID) = Get(
        UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    UserName = protocol.MI_String(UserName)
    Ensure = protocol.MI_String(Ensure)
    FullName = protocol.MI_String(FullName)
    PasswordChangeRequired = protocol.MI_Boolean(PasswordChangeRequired)
    Disabled = protocol.MI_Boolean(Disabled)
    Description = protocol.MI_String(Description)
    Password = protocol.MI_String(Password)
    HomeDirectory = protocol.MI_String(HomeDirectory)
    GroupID = protocol.MI_String(GroupID)
    UserID = protocol.MI_String(UserID)
    arg_names.append('UserID')
    retd = {}
    ld = locals()
    for k in arg_names:
        retd[k] = ld[k]
    return retval, retd
def Inventory_Marshall(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    (UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID) = \
        init_vars(UserName, Ensure, FullName, Description, Password,
                  Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    (retval, Inventory) = GetInventory(
        UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    for d in Inventory:
        d['UserName'] = protocol.MI_String(d['UserName'])
        d['Ensure'] = protocol.MI_String('Present')
        d['FullName'] = protocol.MI_String(d['FullName'])
        d['PasswordChangeRequired'] = protocol.MI_Boolean(d['PasswordChangeRequired'])
        d['Disabled'] = protocol.MI_Boolean(d['Disabled'])
        d['Description'] = protocol.MI_String(d['Description'])
        d['Password'] = protocol.MI_String(d['Password'])
        d['HomeDirectory'] = protocol.MI_String(d['HomeDirectory'])
        d['GroupID'] = protocol.MI_String(d['GroupID'])
        d['UserID'] = protocol.MI_String(d['UserID'])
        d = protocol.MI_Instance(d)
    Inventory = protocol.MI_InstanceA(Inventory)
    retd = {}
    retd["__Inventory"] = Inventory
    return retval, retd
############################################################
# Begin user defined DSC functions
############################################################
def SetShowMof(a):
    global show_mof
    show_mof = a
def ShowMof(op, UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    if not show_mof:
        return
    mof = ''
    mof += op + ' nxUser MyUser \n'
    mof += '{\n'
    mof += '    UserName = "' + UserName + '"\n'
    mof += '    Ensure = "' + Ensure + '"\n'
    mof += '    FullName = "' + FullName + '"\n'
    mof += '    Description = "' + Description + '"\n'
    mof += '    Password = "' + Password + '"\n'
    mof += '    Disabled = ' + str(Disabled) + '\n'
    mof += '    PasswordChangeRequired = ' + str(PasswordChangeRequired) + '\n'
    mof += '    HomeDirectory = "' + HomeDirectory + '"\n'
    mof += '    GroupID = "' + str(GroupID) + '"\n'
    mof += '}\n'
    f = open('./test_mofs.log', 'a')
    Print(mof, file=f)
    LG().Log('INFO', mof)
    f.close()
def Print(s, file=sys.stdout):
    file.write(s + '\n')
def opened_w_error(filename, mode="r"):
    """
    This context ensures the file is closed.
    """
    try:
        f = open(filename, mode=mode)
    except IOError, err:
        return None, err
    return f, None
userdel_path = "/usr/sbin/userdel"
useradd_path = "/usr/sbin/useradd"
usermod_path = "/usr/sbin/usermod"
chage_path = "/usr/bin/chage"
def ReadPasswd(filename):
    f, error = opened_w_error(filename, 'rb')
    if error:
        Print("Exception opening file " + filename + " Error: " + str(error), file=sys.stderr)
        LG().Log('ERROR', "Exception opening file " + filename + " Error: " + str(error))
        return None
    else:
        lines = f.read().split("\n")
        f.close()
    entries = dict()
    for line in lines:
        tokens = line.split(":")
        if len(tokens) > 1:
            entries[tokens[0]] = tokens[1:]
    return entries
def PasswordExpired(shadow_entry):
    # No entries for the "last" field means Password is Expired.
    if shadow_entry[1] == "":
        return True
    # Passwords must be changed if their "last" day is 0
    if shadow_entry[1] == "0":
        return True
    # "99999" means "never expire"
    if shadow_entry[3] == "99999" or shadow_entry[3] == "" :
        return False
    day_0 = datetime.datetime.utcfromtimestamp(0)
    day_now = datetime.datetime.today()
    days_since_day_0 = (day_now - day_0).days
    days_since_last_password_change = days_since_day_0 - int(shadow_entry[1])
    number_of_days_password_is_valid_for = int(shadow_entry[3])
    if days_since_last_password_change > number_of_days_password_is_valid_for:
        return True
    return False
def Set(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    ShowMof('SET', UserName, Ensure, FullName, Description, Password,
            Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    passwd_entries = None
    shadow_entries = None
    passwd_entries = ReadPasswd("/etc/passwd")
    if passwd_entries is None:
        return [-1]
    shadow_entries = ReadPasswd("/etc/shadow")
    if shadow_entries is None:
        return [-1]
    old_passwd_entries = passwd_entries
    usermod_string = ""
    usermodonly_string = ""
    if Ensure == "absent":
        exit_code = os.system(userdel_path + " " + UserName)
    else:
        usermod_string = ""
        if FullName or Description:
            usermod_string += " -c \""
            if FullName:
                usermod_string += FullName
            if Description:
                usermod_string += "," + Description
            usermod_string += "\""
        if HomeDirectory:
            usermod_string += " -d \"" + HomeDirectory + "\" -m "
        if GroupID:
            usermod_string += " -g " + GroupID
        if UserName not in passwd_entries:
            exit_code = os.system(
                useradd_path + " " + usermod_string + " " + UserName)
            if exit_code is not 0:
                return [exit_code]
            if len(usermodonly_string) > 0:
                exit_code = os.system(
                    usermod_path + " " + usermodonly_string + " " + UserName)
        else:
            Print(usermod_string, file=sys.stderr)
            LG().Log('INFO', usermod_string)
            if len(usermodonly_string + usermod_string) > 0:
                exit_code = os.system(
                    usermod_path + " " + usermodonly_string + usermod_string + " " + UserName)
        disabled_user_string = ""
        usermod_string = ""
        if Disabled is True:
            disabled_user_string = "!"
        if len(Password) > 0:
            usermod_string += " -p \"" + disabled_user_string + \
                Password.replace("$", "\$") + "\""
        elif Disabled is True:
            usermodonly_string += " -L"
        elif Disabled is False:
            passwd_entries = ReadPasswd("/etc/passwd")
            if passwd_entries is None:
                return [-1]
            shadow_entries = ReadPasswd("/etc/shadow")
            if shadow_entries is None:
                return [-1]
            if UserName in shadow_entries:
                cur_pass = shadow_entries[UserName][0]
                if cur_pass == "!!":
                    Print("Unable to unlock user: " + UserName +
                          ".  Password is not set.", file=sys.stderr)
                    LG().Log('ERROR', "Unable to unlock user: " +
                             UserName + ".  Password is not set.")
                    return [-1]
                elif cur_pass[0] == '!':
                    if len(cur_pass) > 1:
                        usermodonly_string += " -U"
                    else:
                        Print("Unable to unlock user: " + UserName +
                              ".  Doing so would result in a passwordless account.", file=sys.stderr)
                        LG().Log('ERROR', "Unable to unlock user: " + UserName +
                                 ".  Doing so would result in a passwordless account.")
                        return [-1]
        Print(usermod_string, file=sys.stderr)
        LG().Log('INFO', usermod_string)
        if len(usermodonly_string + usermod_string) > 0:
            exit_code = os.system(
                usermod_path + " " + usermodonly_string + usermod_string + " " + UserName)
        # force password change only if we created the account
        if PasswordChangeRequired is True and UserName not in old_passwd_entries:
            exit_code = os.system(chage_path + " -d  0 " + UserName)
    return [exit_code]
def Test(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    ShowMof('TEST', UserName, Ensure, FullName, Description, Password,
            Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    passwd_entries = None
    shadow_entries = None
    passwd_entries = ReadPasswd("/etc/passwd")
    if passwd_entries is None:
        return [-1]
    shadow_entries = ReadPasswd("/etc/shadow")
    if shadow_entries is None:
        return [-1]
    if not Ensure:
        Ensure = "present"
    if Ensure == "absent":
        if UserName not in passwd_entries:
            return [0]
        else:
            Print(UserName + " in passwd_entries", file=sys.stderr)
            LG().Log('ERROR', UserName + " in passwd_entries")
            return [-1]
    elif Ensure == "present":
        if UserName not in passwd_entries:
            Print(UserName + " not in passwd_entries", file=sys.stderr)
            LG().Log('ERROR', UserName + " not in passwd_entries")
            return [-1]
        if UserName not in shadow_entries:
            Print(UserName + " not in shadow_entries", file=sys.stderr)
            LG().Log('ERROR', UserName + " not in shadow_entries")
            return [-1]
        if len(passwd_entries[UserName]) < 6:
            Print("Unable to read /etc/passwd entry for username: " +
                  UserName, file=sys.stderr)
            LG().Log(
                'ERROR', "Unable to read /etc/passwd entry for username: " + UserName)
            return [-1]
        if len(shadow_entries[UserName]) < 8:
            Print("Unable to read /etc/shadow entry for username: " +
                  UserName, file=sys.stderr)
            LG().Log(
                'ERROR', "Unable to read /etc/shadow entry for username: " + UserName)
            return [-1]
        extra_fields = passwd_entries[UserName][3].split(",")
        if FullName and extra_fields[0] != FullName:
            Print("Incorrect full name (" + extra_fields[
                  0] + "), should be: " + FullName + ", for username: " + UserName, file=sys.stderr)
            LG().Log('ERROR', "Incorrect full name (" +
                     extra_fields[0] + "), should be: " + FullName + ", for username: " + UserName)
            return [-1]
        if Description:
            if len(extra_fields) < 2:
                Print("There is no description.", file=sys.stderr)
                LG().Log('ERROR', "There is no description.")
                return [-1]
            elif extra_fields[1] != Description:
                Print(
                    "Incorrect description for username: " + UserName, file=sys.stderr)
                LG().Log(
                    'ERROR', "Incorrect description for username: " + UserName)
                return [-1]
        if HomeDirectory and passwd_entries[UserName][4] != HomeDirectory:
            Print("Home directories do not match", file=sys.stderr)
            LG().Log('ERROR', "Home directories do not match")
            return [-1]
        if GroupID and passwd_entries[UserName][2] != GroupID:
            Print("GroupID does not match", file=sys.stderr)
            LG().Log('ERROR', "GroupID does not match")
            return [-1]
        if len(Password) > 0:
            read_password = shadow_entries[UserName][0]
            if len(read_password) is 0:
                Print("Password does not match", file=sys.stderr)
                LG().Log('ERROR', "Password does not match")
                return [-1]
            if read_password[0] == "!":
                read_password = read_password[1:]
            if read_password != Password:
                Print("Password does not match", file=sys.stderr)
                LG().Log('ERROR', "Password does not match")
                return [-1]
        if PasswordChangeRequired is True and not PasswordExpired(shadow_entries[UserName]):
            Print(
                "PasswordChangeRequired is True and the password is not expired.", file=sys.stderr)
            LG().Log(
                'ERROR', "PasswordChangeRequired is True and the password is not expired.")
            return [-1]
        elif PasswordChangeRequired is False and PasswordExpired(shadow_entries[UserName]):
            Print(
                "PasswordChangeRequired is False and the password is expired.", file=sys.stderr)
            LG().Log(
                'ERROR', "PasswordChangeRequired is False and the password is expired.")
            return [-1]
        if Disabled is True and shadow_entries[UserName][0][0] != "!":
            Print("Account not disabled", file=sys.stderr)
            LG().Log('ERROR', "Account not disabled")
            return [-1]
        if Disabled is False and shadow_entries[UserName][0][0] == "!":
            Print("Account disabled", file=sys.stderr)
            LG().Log('ERROR', "Account disabled")
            return [-1]
    return [0]
def Get(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    ShowMof('GET', UserName, Ensure, FullName, Description, Password,
            Disabled, PasswordChangeRequired, HomeDirectory, GroupID)
    UserID = ''
    passwd_entries = None
    shadow_entries = None
    passwd_entries = ReadPasswd("/etc/passwd")
    if passwd_entries is None:
        return [-1, UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID, UserID]
    shadow_entries = ReadPasswd("/etc/shadow")
    if shadow_entries is None:
        return [-1, UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID, UserID]
    exit_code = 0
    if UserName not in passwd_entries:
        FullName = Description = Password = HomeDirectory = GroupID = ""
        return [exit_code, UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID, UserID]
    extra_fields = passwd_entries[UserName][3].split(",")
    FullName = extra_fields[0]
    if len(extra_fields) > 1:
        Description = extra_fields[1]
    HomeDirectory = passwd_entries[UserName][4]
    UserID = passwd_entries[UserName][1]
    GroupID = passwd_entries[UserName][2]
    Password = shadow_entries[UserName][0]
    Disabled = False
    if len(Password) > 0:
        if Password[0] == "!":
            Disabled = True
    Password = '' # not showing the password.
    if PasswordExpired(shadow_entries[UserName]):
        PasswordChangeRequired = True
    else:
        PasswordChangeRequired = False
    return [exit_code, UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID, UserID]
def GetInventory(UserName, Ensure, FullName, Description, Password, Disabled, PasswordChangeRequired, HomeDirectory, GroupID):
    Inventory=[]
    passwd_entries = None
    passwd_entries = ReadPasswd("/etc/passwd")
    if passwd_entries is None:
        return [-1, Inventory]
    exit_code = 0
    d={}
    for Uname in passwd_entries.keys():
        if len(UserName) and not fnmatch.fnmatch(Uname,UserName):
            continue
        d['UserName'] = Uname
        extra_fields = passwd_entries[Uname][3].split(",")
        d['FullName'] = extra_fields[0]
        if len(FullName) and not fnmatch.fnmatch(d['FullName'],FullName):
            continue
        d['Description'] = ''
        if len(extra_fields) > 1:
            d['Description'] = extra_fields[1]
        if len(Description) and not fnmatch.fnmatch(d['Description'],Description):
            continue
        d['HomeDirectory'] = passwd_entries[Uname][4]
        d['UserID'] = passwd_entries[Uname][1]
        d['GroupID'] = passwd_entries[Uname][2]
        d['Disabled'] = False
        d['Password'] = '' # not showing the password.
        d['PasswordChangeRequired'] = False
        Inventory.append(copy.deepcopy(d))
    return [exit_code, Inventory]