Documentationcurrent version
Help us improve the docs by fixing typos and proposing enhancements.


Action "system.user"

Create or modify a Unix user.

If the user home is provided, its parent directory will be created with root ownerships and 0644 permissions unless it already exists.

Callback parameters

  • $status
    Value is "true" if user was created or modified.


const {$status} = await nikita.system.user({
  name: 'a_user',
  system: true,
  uid: 490,
  gid: 10,
  comment: 'A System User'
console.log(`User created: ${$status}`)

The result of the above action can be viewed with the command cat /etc/passwd | grep myself producing an output similar to "a_user:x:490:490:A System User:/home/a_user:/bin/bash". You can also check you are a member of the "wheel" group (gid of "10") with the command id a\_user producing an output similar to "uid=490(hive) gid=10(wheel) groups=10(wheel)".


on_action = ({config}) ->
    when true = '/bin/sh'
    when false = '/sbin/nologin'
  config.groups = config.groups.split ',' if typeof config.groups is 'string'

Schema definitions

definitions =
    type: 'object'
        type: 'string'
        description: '''
        Short description of the login.
        type: 'integer'
        description: '''
        The date on which the user account is disabled.
        type: 'integer'
        description: '''
        Group name or number of the user´s initial login group.
        type: 'array'
        items: type: 'string'
        description: '''
        List of supplementary groups which the user is also a member of.
        type: 'string'
        description: '''
        Value for the user´s login directory, default to the login name
        appended to "BASE_DIR".
        type: 'integer'
        description: '''
        The number of days after a password has expired before the account
        will be disabled.
        type: 'string'
        description: '''
        Login name of the user.
        type: 'boolean'
        description: '''
        Disable ownership on home directory which default to the "uid" and
        "gid" config, default is "false".
        type: 'string'
        description: '''
        The unencrypted password.
        type: 'boolean'
        default: true
        description: '''
        Synchronize password
        # oneOf: [
        #   type: 'boolean'
        # ,
        #   type: 'string'
        # ]
        type: ['boolean', 'string']
        default: '/bin/sh'
        description: '''
        Path to the user shell, set to "/sbin/nologin" if `false` and "/bin/sh"
        if `true` or `undefined`.
        type: 'string'
        description: '''
        The skeleton directory, which contains files and directories to be
        copied in the user´s home directory, when the home directory is
        created by useradd.
        type: 'boolean'
        description: '''
        Create a system account, such user are not created with a home by
        default, set the "home" option if we it to be created.
        type: 'integer'
        description: '''
        Numerical value of the user´s ID, must not exist.
    required: ['name']


handler = ({metadata, config, tools: {log}}) ->
  log message: "Entering user", level: 'DEBUG'
  config.system ?= false
  config.gid ?= null
  config.password_sync ?= true
  throw Error "Invalid option 'shell': #{JSON.strinfigy}" if typeof isnt 'string'
  user_info = groups_info = null
  {users} = await
  user_info = users[]
  log if user_info
  then message: "Got user information for #{JSON.stringify}", level: 'DEBUG', module: 'nikita/lib/system/group'
  else message: "User #{JSON.stringify} not present", level: 'DEBUG', module: 'nikita/lib/system/group'
  # Get group information if
  # * user already exists
  # * we need to compare groups membership
  {groups} = await
    $if: user_info and config.groups
  groups_info = groups
  log message: "Got group information for #{JSON.stringify}", level: 'DEBUG' if groups_info
  if config.home
      $unless_exists: path.dirname config.home
      target: path.dirname config.home
      uid: 0
      gid: 0
      mode: 0o0644 # Same as '/home'
  unless user_info
    await @execute [
      code_skipped: 9
      command: [
        '-r' if config.system
        '-M' unless config.home
        '-m' if config.home
        "-d #{config.home}" if config.home
        "-s #{}" if
        "-c #{utils.string.escapeshellarg config.comment}" if config.comment
        "-u #{config.uid}" if config.uid
        "-g #{config.gid}" if config.gid
        "-e #{config.expiredate}" if config.expiredate
        "-f #{config.inactive}" if config.inactive
        "-G #{config.groups.join ','}" if config.groups
        "-k #{config.skel}" if config.skel
        ].join ' '
      $if: config.home
      command: "chown #{}. #{config.home}"
    log message: "User defined elsewhere than '/etc/passwd', exit code is 9", level: 'WARN'
    changed = []
    for k in ['uid', 'home', 'shell', 'comment', 'gid']
      changed.push k if config[k]? and user_info[k] isnt config[k]
    if config.groups then for group in config.groups
      throw Error "Group does not exist: #{group}" unless groups_info[group]
      changed.push 'groups' if groups_info[group].users.indexOf( is -1
    log if changed.length
    then message: "User #{} modified", level: 'WARN', module: 'nikita/lib/system/user/add'
    else message: "User #{} not modified", level: 'DEBUG', module: 'nikita/lib/system/user/add'
      await @execute
        $if: changed.length
        command: [
          "-d #{config.home}" if config.home
          "-s #{}" if
          "-c #{utils.string.escapeshellarg config.comment}" if config.comment?
          "-g #{config.gid}" if config.gid
          "-G #{config.groups.join ','}" if config.groups
          "-u #{config.uid}" if config.uid
        ].join ' '
    catch err
      if err.exit_code is 8
        throw Error "User #{} is logged in"
      else throw err
    if config.home and (config.uid or config.gid)
      await @fs.chown
        $if_exists: config.home
        $unless: config.no_home_ownership
        target: config.home
        uid: config.uid
        gid: config.gid
  # TODO, detect changes in password
  # echo #{config.password} | passwd --stdin #{}
  if config.password_sync and config.password
    {$status} = await @execute
      command: """
      hash=$(echo #{config.password} | openssl passwd -1 -stdin)
      usermod --pass="$hash" #{}
      # arch_chroot: config.arch_chroot
      # rootdir: config.rootdir
      # sudo: config.sudo
    log message: "Password modified", level: 'WARN' if $status


module.exports =
  handler: handler
    on_action: on_action
    argument_to_config: 'name'
    definitions: definitions


path = require 'path'
utils = require '../utils'
Edit on GitHub

Nikita is an open source project hosted on GitHub and developed by Adaltas.