class SyncWrap::Users

Provision developer user accounts, sudo access, and synchronize home directory files.

Attributes

exclude_users[RW]

Set of users to exclude from synchronization (default: [])

home_users[RW]

The list of user names to install. If default nil, #home_users will be determined by the set of home directories found in #local_home_root - exclude_users.

lenient_host_key[RW]

Use the StrictHostKeyChecking=no ssh option when connected to a newly created host (whose key will surely not be known yet.) (Default: true)

local_home_root[RW]

Local home root directory, to be resolved against sync_paths. (Default: home)

ssh_access_timeout[RW]

Users should be the first component to attempt ssh access. On new host creation, install may be run before the ssh port is actually available (boot completed, etc.). This provides an additional timeout in seconds for establishing the first ssh session. (default: 180 seconds)

ssh_user[RW]

If set, override the :ssh_user for this Component install, since typically the 'normal' user (i.e your developer account) has not yet been created or given sudo access. (Default: nil)

ssh_user_pem[RW]

A PEM file for the ssh_user. A relative path in interpreted as relative to the sync file from which this component is created. If the pem file is not found, a warning will be issued and #ssh_user and #ssh_user_pem will not be used. (Default: nil)

Public Class Methods

new( opts = {} ) click to toggle source
Calls superclass method SyncWrap::Component.new
# File lib/syncwrap/components/users.rb, line 65
def initialize( opts = {} )
  @home_users = nil
  @local_home_root = "home"
  @exclude_users = []
  @ssh_user = nil
  @ssh_user_pem = nil
  @ssh_access_timeout = 180.0
  @lenient_host_key = true
  super

  if @ssh_user_pem
    @ssh_user_pem =
      relativize( path_relative_to_caller( @ssh_user_pem, caller ) )
    unless File.exist?( @ssh_user_pem )
      warn( "WARNING: #{@ssh_user_pem} not found, " +
            "Users will not use #{@ssh_user}.\n" +
            "         Expect failures if user #{ENV['USER']} isn't already a sudoer." )
      @ssh_user = nil
      @ssh_user_pem = nil
    end
  end
end

Public Instance Methods

create_user( user ) click to toggle source

Create user if not already present

# File lib/syncwrap/components/users.rb, line 146
def create_user( user )
  sudo <<-SH
    if ! id #{user} >/dev/null 2>&1; then
      useradd -s /bin/bash -m #{user}
    fi
  SH
end
ensure_ssh_access() click to toggle source
# File lib/syncwrap/components/users.rb, line 124
def ensure_ssh_access
  flags = ssh_flags
  if lenient_host_key
    flags[ :ssh_options ] ||= {}
    flags[ :ssh_options ][ 'StrictHostKeyChecking' ] = 'no'
  end

  start = Time.now
  loop do
    accept  = [0]
    # Allow non-sucess until timeout
    # 255: ssh (i.e. can't connect, sshd not yet up)
    # 1: sudo error (user_data sudoers update has not yet completed)
    accept += [1, 255] unless ( Time.now - start ) >= ssh_access_timeout

    code,_ = capture( 'sudo true', flags.merge( accept: accept ) )
    break if code == 0
    sleep 1 # ssh timeouts also apply, but make sure we don't spin
  end
end
fix_home_permissions( user ) click to toggle source
# File lib/syncwrap/components/users.rb, line 160
def fix_home_permissions( user )
  sudo <<-SH
    if [ -e '/home/#{user}/.ssh' ]; then
      chmod 700 /home/#{user}/.ssh
      chmod -R o-rwx /home/#{user}/.ssh
    fi
  SH
end
install() click to toggle source
# File lib/syncwrap/components/users.rb, line 88
def install
  ensure_ssh_access if state[ :just_created ] && ssh_access_timeout > 0

  rdir = find_source( local_home_root )
  users = home_users
  users ||= rdir && Dir.entries( rdir ).select do |d|
    ( d !~ /^\./ && File.directory?( File.join( rdir, d ) ) )
  end
  users ||= []
  users -= exclude_users

  users.each do |u|
    create_user( u )
    set_sudoers( u )
  end

  # Some distro's, like Debian, don't come with rsync installed so
  # need to install it here. For backward compatibly, only do
  # this if dist_install is defined (i.e. Distro component before
  # self.)
  if !users.empty? && respond_to?( :dist_install )
    dist_install( 'rsync',
                  ssh_flags.merge( minimal: true, check_install: true ) )
  end

  users.each do |u|
    sync_home_files( u )
  end

  users.each do |u|
    fix_home_permissions( u )
  end

  #FIXME: Add special case for 'root' user?
end
set_sudoers( user ) click to toggle source
# File lib/syncwrap/components/users.rb, line 169
def set_sudoers( user )
  sudo sudoers_d_commands( user )
end
sync_home_files( user ) click to toggle source
# File lib/syncwrap/components/users.rb, line 154
def sync_home_files( user )
  rput( "#{local_home_root}/#{user}", user: user )
rescue SourceNotFound
  false
end

Protected Instance Methods

rput( *args ) click to toggle source
Calls superclass method SyncWrap::Component#rput
# File lib/syncwrap/components/users.rb, line 179
def rput( *args )
  opts = args.last.is_a?( Hash ) ? ssh_flags.merge( args.pop ) : ssh_flags
  super( *args, opts )
end
sh( command, opts = {}, &block ) click to toggle source
Calls superclass method SyncWrap::Component#sh
# File lib/syncwrap/components/users.rb, line 175
def sh( command, opts = {}, &block )
  super( command, ssh_flags.merge( opts ), &block )
end
ssh_flags() click to toggle source
# File lib/syncwrap/components/users.rb, line 184
def ssh_flags
  flags = {}
  if ssh_user
    flags[ :ssh_user ] = ssh_user
    if ssh_user_pem
      flags[ :ssh_user_pem ] = ssh_user_pem
      flags[ :ssh_options ] = { 'IdentitiesOnly' => 'yes',
                                'PasswordAuthentication' => 'no' }
    end
  end
  flags
end