<commit_before># -*- coding: utf-8 -*-
'''
Manage Mac OSX local directory passwords and policies.

Note that it is usually better to apply password policies through the creation of a configuration profile.
'''
from __future__ import absolute_import

import os
import base64
import salt.utils

try:
    from passlib.utils import pbkdf2, ab64_encode, ab64_decode
    HAS_PASSLIB = True
except ImportError:
    HAS_PASSLIB = False


def __virtual__():
    if HAS_PASSLIB and salt.utils.is_darwin():
        return True
    else:
        return False


def _pl_salted_sha512_pbkdf2_from_string(strvalue, salt_bin=None, iterations=1000):
    '''
    Create a PBKDF2-SHA512 hash with a 128 byte key length.
    The standard passlib.hash.pbkdf2_sha512 functions assume a 64 byte key length which does not match OSX's
    implementation.

    :param strvalue: The string to derive the hash from
    :param salt: The (randomly generated) salt
    :param iterations: The number of iterations, for Mac OS X it's normally between 23000-25000? need to confirm.
    :return: (binary digest, binary salt, number of iterations used)
    '''
    if salt_bin is None:
        salt_bin = os.urandom(32)

    key_length = 128
    hmac_sha512, dsize = pbkdf2.get_prf("hmac-sha512")
    digest_bin = pbkdf2.pbkdf2(strvalue, salt_bin, iterations, key_length, hmac_sha512)

    return digest_bin, salt_bin, iterations

def info(name):
    '''
    Return information for the specified user
<commit_msg>Attempt to read ShadowHashData without introducing dependencies. No solid implementation for reading the hash yet.<commit_after># -*- coding: utf-8 -*-
'''
Manage Mac OSX local directory passwords and policies.

Note that it is usually better to apply password policies through the creation of a configuration profile.

Tech Notes:
Usually when a password is changed by the system, there's a responsibility to check the hash list and generate hashes
for each. Many osx password changing scripts/modules only deal with the SHA-512 PBKDF2 hash when working with the local
node.
'''
from __future__ import absolute_import

import os
import base64
import salt.utils
import string
import binascii
import tempfile

try:
    from passlib.utils import pbkdf2, ab64_encode, ab64_decode
    HAS_PASSLIB = True
except ImportError:
    HAS_PASSLIB = False


def __virtual__():
    if HAS_PASSLIB and salt.utils.is_darwin():
        return True
    else:
        return False


def _pl_salted_sha512_pbkdf2_from_string(strvalue, salt_bin=None, iterations=1000):
    '''
    Create a PBKDF2-SHA512 hash with a 128 byte key length.
    The standard passlib.hash.pbkdf2_sha512 functions assume a 64 byte key length which does not match OSX's
    implementation.

    :param strvalue: The string to derive the hash from
    :param salt: The (randomly generated) salt
    :param iterations: The number of iterations, for Mac OS X it's normally between 23000-25000? need to confirm.
    :return: (binary digest, binary salt, number of iterations used)
    '''
    if salt_bin is None:
        salt_bin = os.urandom(32)

    key_length = 128
    hmac_sha512, dsize = pbkdf2.get_prf("hmac-sha512")
    digest_bin = pbkdf2.pbkdf2(strvalue, salt_bin, iterations, key_length, hmac_sha512)

    return digest_bin, salt_bin, iterations

def read_shadowhash(name):
    '''
    Read the existing hash for the named user.

    name
        The username associated with the local directory user.
    '''
    # Note: I've tried as best I could to keep third party dependencies out of this module.
    # You could just as easily use PyObjC or plistlib, but I felt that portability was important.
    tmpdir = tempfile.mkdtemp('.shadowhash')
    tmpfile = os.path.join(tmpdir, 'shadowhash.plist')

    # We have to strip the output string, convert hex back to binary data, read that plist and get our specific
    # key/value property to find the hash. I.E there's a lot of unwrapping to do.
    data = __salt__['cmd.run']('/usr/bin/dscl . read /Users/{0} ShadowHashData'.format(name))
    parts = string.split(data, '\n')
    plist_hex = string.replace(parts[1], ' ', '')
    f = file(tmpfile, 'w')
    f.write(binascii.unhexlify(plist_hex))
    f.close()
    return tmpfile



def info(name):
    '''
    Return information for the specified user

{'fraction_non_alphanumeric': 0.07407407407407407, 'fraction_numerical': 0.027486606102958305, 'mean_word_length': 3.602357984994641, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6776899', 'n_tokens_mistral': 1386, 'n_tokens_neox': 1273, 'n_words': 493}