text: """
  Manage the password database on Solaris systems
  
  .. important::
      If you feel that Salt should be using this module to manage passwords on a
      minion, and it is using a different module (or gives an error similar to
      *'shadow.info' is not available*), see :ref:`here
      <module-provider-override>`.
  """
  
  import os
  
  import salt.utils.files
  from salt.exceptions import CommandExecutionError
  
  try:
      import spwd
  
      HAS_SPWD = True
  except ImportError:
      # SmartOS joyent_20130322T181205Z does not have spwd
      HAS_SPWD = False
      try:
          import pwd
      except ImportError:
          pass  # We're most likely on a Windows machine.
  
  
  try:
      import salt.utils.pycrypto
  
      HAS_CRYPT = True
  except ImportError:
      HAS_CRYPT = False
  
  
  # Define the module's virtual name
  __virtualname__ = "shadow"
  
  
  def __virtual__():
      """
      Only work on POSIX-like systems
      """
      if __grains__.get("kernel", "") == "SunOS":
          return __virtualname__
      return (
          False,
          "The solaris_shadow execution module failed to load: only available on Solaris systems.",
      )
  
  
  def default_hash():
      """
      Returns the default hash used for unset passwords
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.default_hash
      """
      return "!"
  
  
  def info(name):
      """
      Return information for the specified user
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.info root
      """
      if HAS_SPWD:
          try:
              data = spwd.getspnam(name)
              ret = {
                  "name": data.sp_nam,
                  "passwd": data.sp_pwd,
                  "lstchg": data.sp_lstchg,
                  "min": data.sp_min,
                  "max": data.sp_max,
                  "warn": data.sp_warn,
                  "inact": data.sp_inact,
                  "expire": data.sp_expire,
              }
          except KeyError:
              ret = {
                  "name": "",
                  "passwd": "",
                  "lstchg": "",
                  "min": "",
                  "max": "",
                  "warn": "",
                  "inact": "",
                  "expire": "",
              }
          return ret
  
      # SmartOS joyent_20130322T181205Z does not have spwd, but not all is lost
      # Return what we can know
      ret = {
          "name": "",
          "passwd": "",
          "lstchg": "",
          "min": "",
          "max": "",
          "warn": "",
          "inact": "",
          "expire": "",
      }
  
      try:
          data = pwd.getpwnam(name)
          ret.update({"name": name})
      except KeyError:
          return ret
  
      # To compensate for lack of spwd module, read in password hash from /etc/shadow
      s_file = "/etc/shadow"
      if not os.path.isfile(s_file):
          return ret
      with salt.utils.files.fopen(s_file, "r") as ifile:
          for line in ifile:
              comps = line.strip().split(":")
              if comps[0] == name:
                  ret.update({"passwd": comps[1]})
  
      # For SmartOS `passwd -s <username>` and the output format is:
      #   name status mm/dd/yy min max warn
      #
      # Fields:
      #  1. Name: username
      #  2. Status:
      #      - LK: locked
      #      - NL: no login
      #      - NP: No password
      #      - PS: Password
      #  3. Last password change
      #  4. Minimum age
      #  5. Maximum age
      #  6. Warning period
  
      output = __salt__["cmd.run_all"]("passwd -s {}".format(name), python_shell=False)
      if output["retcode"] != 0:
          return ret
  
      fields = output["stdout"].split()
      if len(fields) == 2:
          # For example:
          #   root      NL
          return ret
      # We have all fields:
      #   buildbot L 05/09/2013 0 99999 7
      ret.update(
          {
              "name": data.pw_name,
              "lstchg": fields[2],
              "min": int(fields[3]),
              "max": int(fields[4]),
              "warn": int(fields[5]),
              "inact": "",
              "expire": "",
          }
      )
      return ret
  
  
  def set_maxdays(name, maxdays):
      """
      Set the maximum number of days during which a password is valid. See man
      passwd.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.set_maxdays username 90
      """
      pre_info = info(name)
      if maxdays == pre_info["max"]:
          return True
      cmd = "passwd -x {} {}".format(maxdays, name)
      __salt__["cmd.run"](cmd, python_shell=False)
      post_info = info(name)
      if post_info["max"] != pre_info["max"]:
          return post_info["max"] == maxdays
  
  
  def set_mindays(name, mindays):
      """
      Set the minimum number of days between password changes. See man passwd.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.set_mindays username 7
      """
      pre_info = info(name)
      if mindays == pre_info["min"]:
          return True
      cmd = "passwd -n {} {}".format(mindays, name)
      __salt__["cmd.run"](cmd, python_shell=False)
      post_info = info(name)
      if post_info["min"] != pre_info["min"]:
          return post_info["min"] == mindays
      return False
  
  
  def gen_password(password, crypt_salt=None, algorithm="sha512"):
      """
      .. versionadded:: 2015.8.8
  
      Generate hashed password
  
      .. note::
  
          When called this function is called directly via remote-execution,
          the password argument may be displayed in the system's process list.
          This may be a security risk on certain systems.
  
      password
          Plaintext password to be hashed.
  
      crypt_salt
          Crpytographic salt. If not given, a random 8-character salt will be
          generated.
  
      algorithm
          The following hash algorithms are supported:
  
          * md5
          * blowfish (not in mainline glibc, only available in distros that add it)
          * sha256
          * sha512 (default)
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.gen_password 'I_am_password'
          salt '*' shadow.gen_password 'I_am_password' crypt_salt='I_am_salt' algorithm=sha256
      """
      if not HAS_CRYPT:
          raise CommandExecutionError(
              "gen_password is not available on this operating system "
              'because the "crypt" python module is not available.'
          )
      return salt.utils.pycrypto.gen_hash(crypt_salt, password, algorithm)
  
  
  def del_password(name):
      """
      .. versionadded:: 2015.8.8
  
      Delete the password from name user
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.del_password username
      """
      cmd = "passwd -d {}".format(name)
      __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="quiet")
      uinfo = info(name)
      return not uinfo["passwd"]
  
  
  def set_password(name, password):
      """
      Set the password for a named user. The password must be a properly defined
      hash, the password hash can be generated with this command:
      ``openssl passwd -1 <plaintext password>``
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.set_password root $1$UYCIxa628.9qXjpQCjM4a..
      """
      s_file = "/etc/shadow"
      ret = {}
      if not os.path.isfile(s_file):
          return ret
      lines = []
      with salt.utils.files.fopen(s_file, "r") as ifile:
          for line in ifile:
              comps = line.strip().split(":")
              if comps[0] != name:
                  lines.append(line)
                  continue
              comps[1] = password
              line = ":".join(comps)
              lines.append("{}\n".format(line))
      with salt.utils.files.fopen(s_file, "w+") as ofile:
          lines = [salt.utils.stringutils.to_str(_l) for _l in lines]
          ofile.writelines(lines)
      uinfo = info(name)
      return uinfo["passwd"] == password
  
  
  def set_warndays(name, warndays):
      """
      Set the number of days of warning before a password change is required.
      See man passwd.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' shadow.set_warndays username 7
      """
      pre_info = info(name)
      if warndays == pre_info["warn"]:
          return True
      cmd = "passwd -w {} {}".format(warndays, name)
      __salt__["cmd.run"](cmd, python_shell=False)
      post_info = info(name)
      if post_info["warn"] != pre_info["warn"]:
          return post_info["warn"] == warndays
      return False
  