require 'puppet/provider/nameservice/pw'
require 'open3'

Puppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService::PW do
  desc "User management via `pw` on FreeBSD and DragonFly BSD."

  commands :pw => "pw"
  has_features :manages_homedir, :allows_duplicates, :manages_passwords, :manages_expiry, :manages_shell

  defaultfor :operatingsystem => [:freebsd, :dragonfly]
  confine    :operatingsystem => [:freebsd, :dragonfly]

  options :home, :flag => "-d", :method => :dir
  options :comment, :method => :gecos
  options :groups, :flag => "-G"
  options :expiry, :method => :expire, :munge => proc { |value|
    value = '0000-00-00' if value == :absent
    value.split("-").reverse.join("-")
  }


  verify :gid, "GID must be an integer" do |value|
    value.is_a? Integer
  end

  verify :groups, "Groups must be comma-separated" do |value|
    value !~ /\s/
  end

  def addcmd
    cmd = [command(:pw), "useradd", @resource[:name]]
    @resource.class.validproperties.each do |property|
      next if property == :ensure or property == :password
      value = @resource.should(property)
      if value and value != ""
        cmd << flag(property) << munge(property,value)
      end
    end

    cmd << "-o" if @resource.allowdupe?
    cmd << "-m" if @resource.managehome?
    cmd
  end

  def modifycmd(param, value)
    if param == :expiry
      # FreeBSD uses DD-MM-YYYY rather than YYYY-MM-DD
      value = value.split("-").reverse.join("-")
    end
    cmd = super(param, value)
    cmd << "-m" if @resource.managehome?
    cmd
  end

  def deletecmd
    cmd = super
    cmd << "-r" if @resource.managehome?
    cmd
  end

  def create
    super

    # Set the password after create if given
    self.password = @resource[:password] if @resource[:password]
  end

  # use pw to update password hash
  def password=(cryptopw)
    Puppet.debug "change password for user '#{@resource[:name]}' method called with hash [redacted]"
    stdin, _, _ = Open3.popen3("pw user mod #{@resource[:name]} -H 0")
    stdin.puts(cryptopw)
    stdin.close
    Puppet.debug "finished password for user '#{@resource[:name]}' method called with hash [redacted]"
  end

  # get password from /etc/master.passwd
  def password
    Puppet.debug "checking password for user '#{@resource[:name]}' method called"
    current_passline = `getent passwd #{@resource[:name]}`
    current_password = current_passline.chomp.split(':')[1] if current_passline
    Puppet.debug "finished password for user '#{@resource[:name]}' method called : [redacted]"
    current_password
  end

  def has_sensitive_data?(property = nil)
    #Check for sensitive values?
    properties = property ? [property] : Puppet::Type.type(:user).validproperties
    properties.any? do |prop|
      p = @resource.parameter(prop)
      p && p.respond_to?(:is_sensitive) && p.is_sensitive
    end
  end

  # Get expiry from system and convert to Puppet-style date
  def expiry
    expiry = self.get(:expiry)
    expiry = :absent if expiry == 0

    if expiry != :absent
      t = Time.at(expiry)
      expiry = "%4d-%02d-%02d" % [t.year, t.month, t.mday]
    end

    expiry
  end
end

