The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl

use 5.10.1;
use strict;
use warnings;

use App::authkeymgr;

use File::Copy;
use File::Slurp;
use File::Spec;

use Getopt::Long;
my $opts = {
  'version' => sub {
    print(
      "authkeys-rebuild - App::authkeymgr-",
       $App::authkeymgr::VERSION,
      "\n",
    );
    exit 0
  },

  'help' => sub {
    print(
      "authkeys-rebuild usage:\n\n",
      "  --help     Display help\n",
      "  --verbose  Display added key paths\n\n",
      
      "  --yestoall Run non-interactively\n\n",
      
      "  --keydir=PATH\n",
      "    Path to directory containing .pub keys\n",
      "  --dest=PATH\n",
      "    Path to output file\n",
    );
    exit 0
  },
};

GetOptions( $opts,
  'help',
  'version',
  'verbose',
  'yestoall',

  'keydir=s',
  'dest|authkeys=s',
);

$|++;

unless ($opts->{keydir}) {
  $ENV{HOME} or die "--keydir not specified and no home directory found\n";

  my $default_keydir = File::Spec->catdir($ENV{HOME}, ".ssh", "pubkeys");

  die "--keydir not specified and no $default_keydir found\n"
    unless -d $default_keydir;

  $opts->{keydir} = $default_keydir;
}

until ( -d $opts->{keydir} ) {
  unless ($opts->{yestoall}) {
    print(
        "The specified --keydir is not a directory.\n",
        "(Path: $opts->{keydir})\n",
        "Specify a valid key directory path, ^C quits.\n",
        "> ",
    );
    my $maybe_keydir = <STDIN>;
    chomp($maybe_keydir);
    $opts->{keydir} = $maybe_keydir;
  } else {
    die "--keydir not a directory: $opts->{keydir}\n";
  }
}

unless ($opts->{dest}) {
  $ENV{HOME} or die "--dest not specified and no home directory found\n";

  my $sshdir = File::Spec->catdir($ENV{HOME}, ".ssh");
  
  my $default_dest = File::Spec->catfile($sshdir, "authorized_keys");

  if ($opts->{yestoall}) {
    print "No --dest specified; defaulting to $default_dest\n";
    $opts->{dest} = $default_dest;
  } else {
    print(
      "No destination (--dest) specified.\n",
      "Enter a path:\n",
      "[$default_dest]  ",
    );
    my $selected_dest = <STDIN>;
    chomp($selected_dest);
    $opts->{dest} = $selected_dest || $default_dest;
  }
}

if (-e $opts->{dest}) {
  my $bakpath = $opts->{dest} . ".bak";
  print(
    "Destination exists: $opts->{dest}\n",
    "Copying to: $bakpath\n",
  );
  copy( $opts->{dest}, $bakpath )
    or die "File copy failure: $!";
  chmod( 0600, $bakpath );  ## just in case ...
}

my @valid_ext = qw/
  pub
  pubkey
/;

say "Searching for pubkeys: $opts->{keydir}";

my @found = App::authkeymgr::findkeys($opts->{keydir});

die "No pubkeys found in $opts->{keydir}\n" unless @found;
say "Found ".scalar @found." pubkeys.";

my $outbuf = "## Generated by authkeys-rebuild ##\n";
PUBKEY: for my $thiskey_path (@found) {
  my $thiskey_content = read_file($thiskey_path, err_mode => 'carp' )
     // next PUBKEY;

  $outbuf .= "## $thiskey_path ##\n"
             . $thiskey_content 
             . "\n" ;
  say "Added: $thiskey_path" if $opts->{verbose};
}

say "Writing: $opts->{dest}";
write_file($opts->{dest}, $outbuf);
chmod( 0600, $opts->{dest} );


__END__

=pod

=head1 NAME

authkeys-rebuild - build new authorized_keys list

=head1 SYNOPSIS

  authkeys-rebuild --help
  
  authkeys-rebuild --keydir=/path/to/pubkeys/
  
  authkeys-rebuild --dest=/path/to/authorized_keys

=head1 DESCRIPTION

Build an authorized_keys file by recursively locating 
C<.pub> public key files under a specified directory.

For example:

  ## Add some users:
  mkdir -p ~/.ssh/pubkeys/users
  mkdir ~/.ssh/pubkeys/users/joe
  cp joe.pub joe-home.pub ~/.ssh/pubkeys/users/joe/
  mkdir ~/.ssh/pubkeys/users/bob
  cp bob.pub ~/.ssh/pubkeys/users/bob/
  
  ## Generate a fresh authorized_keys using defaults:
  authkeys-rebuild --yestoall --verbose
  
  ## Oops, we need to revoke joe:
  rm -r ~/.ssh/pubkeys/users/joe
  authkeys-rebuild --yestoall --verbose

B<authkeys-rebuild> is a cheap hack, the result of some discussion on 
B<#linode> regarding the lack of an AuthorizedKeysDir sshd_config 
directive.

See B<authkeymgr> from this distribution for a complete interactive 
approach to managing authorized key sets.

=head1 AUTHOR

Jon Portnoy <avenj@cobaltirc.org>

=cut