
`krb5_ktadd([goptions], options, callback`
------------------------------------------

Create a new Kerberos principal and an optionnal keytab.

    each = require 'each'
    misc = require './misc'
    conditions = require './misc/conditions'
    child = require './misc/child'
    execute = require './execute'
    chmod = require './chmod'
    chown = require './chown'

`options`           Command options include:
*   `kadmin_server` Address of the kadmin server; optional, use "kadmin.local" if missing.
*   `kadmin_principal`  KAdmin principal name unless `kadmin.local` is used.
*   `kadmin_password`   Password associated to the KAdmin principal.
*   `principal`     Principal to be created.
*   `password`      Password associated to this principal; required if no randkey is provided.
*   `randkey`       Generate a random key; required if no password is provided.
*   `keytab`        Path to the file storing key entries.
*   `ssh`           Run the action on a remote server using SSH, an ssh2 instance or an configuration object used to initialize the SSH connection.
*   `log`           Function called with a log related messages.
*   `stdout`        Writable Stream in which commands output will be piped.
*   `stderr`        Writable Stream in which commands error will be piped.

    module.exports = (goptions, options, callback) ->
      [goptions, options, callback] = misc.args arguments
      misc.options options, (err, options) ->
        return callback err if err
        executed = 0
        each(options)
        .parallel( goptions.parallel )
        .on 'item', (options, next) ->
          return next new Error 'Property principal is required' unless options.principal
          return next new Error 'Property keytab is required' unless options.keytab
          # options.realm ?= options.principal.split('@')[1] # Break cross-realm principals
          options.realm ?= options.kadmin_principal.split('@')[1] if /.*@.*/.test options.kadmin_principal
          modified = false
          do_get = ->
            return do_end() unless options.keytab
            execute
              cmd: "klist -k #{options.keytab}"
              ssh: options.ssh
              log: options.log
              stdout: options.stdout
              stderr: options.stderr
              code_skipped: 1
            , (err, exists, stdout, stderr) ->
              return next err if err
              return do_ktadd() unless exists
              keytab = {}
              for line in stdout.split '\n'
                if match = /^\s*(\d+)\s*(.*)\s*$/.exec line
                  [_, kvno, principal] = match
                  keytab[principal] = kvno
              # Principal is not listed inside the keytab
              return do_ktadd() unless keytab[options.principal]?
              execute
                cmd: misc.kadmin options, "getprinc #{options.principal}"
                ssh: options.ssh
                log: options.log
                stdout: options.stdout
                stderr: options.stderr
              , (err, exists, stdout, stderr) ->
                return err if err
                return do_ktadd() unless -1 is stdout.indexOf 'does not exist'
                vno = null
                for line in stdout.split '\n'
                  if match = /Key: vno (\d+)/.exec line
                    [_, vno] = match
                    break
                return do_chown() if keytab[principal] is vno
                do_ktadd()
          do_ktadd = ->
            execute
              cmd: misc.kadmin options, "ktadd -k #{options.keytab} #{options.principal}"
              ssh: options.ssh
              log: options.log
              stdout: options.stdout
              stderr: options.stderr
            , (err, ktadded) ->
              return next err if err
              modified = true
              do_chown()
          do_chown = () ->
            return do_chmod() if not options.keytab or (not options.uid and not options.gid)
            chown
              ssh: options.ssh
              log: options.log
              destination: options.keytab
              uid: options.uid
              gid: options.gid
            , (err, chowned) ->
              return next err if err
              modified = chowned if chowned
              do_chmod()
          do_chmod = () ->
            return do_end() if not options.keytab or not options.mode
            chmod
              ssh: options.ssh
              log: options.log
              destination: options.keytab
              mode: options.mode
            , (err, chmoded) ->
              return next err if err
              modified = chmoded if chmoded
              do_end()
          do_end = ->
            executed++ if modified
            next()
          conditions.all options, next, do_get
        .on 'both', (err) ->
          callback err, executed