tcmslogin 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #!/usr/bin/env perl
  2. package tcms::login;
  3. use 5.040;
  4. use re '/aasx';
  5. use warnings FATAL => 'all';
  6. use FindBin::libs;
  7. use Getopt::Long;
  8. use Pod::Usage;
  9. use Trog::Auth;
  10. use Trog::Log;
  11. # Don't murder our terminal when done
  12. $ENV{NOHUP} = 1;
  13. =head1 SYNOPSIS
  14. Log a user in and print the resulting session cookie, so that you can hand it
  15. to call.pl and exercise authenticated routes without a browser.
  16. =head2 USAGE
  17. tcmslogin --user foo --password bar
  18. # Which is meant to be used like this:
  19. call.pl GET /admin/wyzzerdd '' "$(tcmslogin --user foo --password bar)"
  20. The cookie is printed to STDOUT and nothing else is, so it is safe to capture.
  21. Everything else the script has to say goes to STDERR.
  22. Note that logging in wastes any session the user already holds -- the session
  23. table keeps one row per user -- so running this will log that user out of their
  24. browser.
  25. =head2 OPTIONS
  26. =over 4
  27. =item --user
  28. The user to log in. Required.
  29. =item --password
  30. That user's password. Leaving the option out, or passing it with no value,
  31. reads it from STDIN instead, which keeps it out of your shell history:
  32. tcmslogin --user foo # prompts, with echo off
  33. tcmslogin --user foo --password # same
  34. echo hunter2 | tcmslogin --user foo
  35. =item --token
  36. The user's current TOTP code, if they have 2FA enabled.
  37. =item --raw
  38. Print just the session id, without the C<tcmslogin=> prefix.
  39. =item --help, --man
  40. Display this output.
  41. =back
  42. =cut
  43. # The logger is set up per-request by the server; a CLI run has to do it itself,
  44. # or the first thing Trog::Auth logs blows up on an undefined $log.
  45. sub _init_logs {
  46. my ($who) = @_;
  47. Trog::Log::log_init( 'logs/tcms.log', 'info' );
  48. $Trog::Log::user = $who // 'nobody';
  49. return;
  50. }
  51. sub _read_password {
  52. my ($prompt) = @_;
  53. my $password;
  54. if ( -t STDIN ) {
  55. print STDERR $prompt;
  56. require Term::ReadKey;
  57. Term::ReadKey::ReadMode('noecho');
  58. $password = <STDIN>;
  59. Term::ReadKey::ReadMode('restore');
  60. print STDERR "\n";
  61. }
  62. else {
  63. $password = <STDIN>;
  64. }
  65. chomp $password if defined $password;
  66. return $password;
  67. }
  68. sub main {
  69. my %options;
  70. Getopt::Long::GetOptionsFromArray(
  71. \@_,
  72. 'user=s' => \$options{user},
  73. 'password:s' => \$options{password},
  74. 'token=s' => \$options{token},
  75. 'raw' => \$options{raw},
  76. 'help|?' => \$options{help},
  77. );
  78. pod2usage( -exitval => 0, -verbose => 1 ) if $options{help};
  79. unless ( $options{user} ) {
  80. print STDERR "--user is required.\n";
  81. return 2;
  82. }
  83. _init_logs( $options{user} );
  84. # Omitted entirely or passed with no value both mean "ask me".
  85. $options{password} = _read_password("Password for '$options{user}': ")
  86. unless $options{password};
  87. unless ( $options{password} ) {
  88. print STDERR "No password provided.\n";
  89. return 2;
  90. }
  91. # mksession() tells us nothing about *why* it said no, by design -- it is
  92. # the same '' for an unknown user, a bad password and a bad TOTP code.
  93. my $session = Trog::Auth::mksession( $options{user}, $options{password}, $options{token}, 'tcmslogin' );
  94. unless ($session) {
  95. print STDERR "Login failed for '$options{user}'.\n";
  96. print STDERR "Check the password, and pass --token if this user has 2FA enabled.\n";
  97. print STDERR "Use bin/tcms-useradd --user $options{user} --password ... to reset it.\n";
  98. return 1;
  99. }
  100. print $options{raw} ? "$session\n" : "tcmslogin=$session\n";
  101. return 0;
  102. }
  103. exit main(@ARGV) unless caller;
  104. 1;