#!/usr/bin/env perl # # svn-win-add-user - add a user on svn.win.tue.nl # # $Id$ use warnings; use strict; use Getopt::Std; use WWW::Mechanize; use WWW::Mechanize::Plugin::FollowMetaRedirect; use MIME::Base64; my ($me) = $0 =~ m#([^/]+)$#; my %opt; getopts( 'hnva:bf:m:r:p:u:', \%opt ); $opt{h} and HELP_MESSAGE(); foreach my $x (qw(a f m)) { if ( $opt{b} ) { defined $opt{$x} and aargh("options -b and -$x do not go together"); } else { defined $opt{$x} or aargh("option -$x is required unless -b is used"); } } @ARGV and aargh('arguments are not allowed, only options; use -h for help'); !$opt{a} || is_valid_username( $opt{a} ) or aargh( 'option -a must specify a username consisting of lower case letters and digits' ); !$opt{f} || is_valid_fullname( $opt{f} ) or aargh('option -m must specify a full user name (at least two parts)'); !$opt{m} || is_valid_emailaddress( $opt{m} ) or aargh('option -m must specify a valid email address'); $opt{b} and require Text::CSV; exit !doitall(); sub HELP_MESSAGE { print STDERR <import; my ( $prompt, $repl ) = @_; open( my $out_fh, '>', '/dev/tty' ) or aargh("cannot write to tty: $!"); open( my $in_fh, '<', '/dev/tty' ) or aargh("cannot read from tty: $!"); my $stdout = *STDOUT; select($out_fh); local $| = 1; # Turn off STDOUT buffering for immediate response print "$prompt: "; ReadMode( 2, $in_fh ); # Change to Raw Mode, disable Ctrl-C my $key; my $pword; while (1) { while ( !defined( $key = ReadKey( -1, $in_fh ) ) ) { } if ( $key =~ /^[\r\n]/ ) { # if Enter was pressed... print $key; # print a newline last; # and get out of here } print( $repl // $key ); $pword .= $key; } ReadMode( 0, $in_fh ); # Reset tty mode before exiting. <==IMPORTANT close($in_fh); select($stdout); $pword; } sub aargh { die join( ' ', "$me: fatal error:", @_ ), "\n"; } sub ehm { warn join( ' ', "$me: warning:", @_ ), "\n"; } sub ehm_v { $opt{v} and ehm(@_); } sub is_valid_username { grep { /^[a-z0-9]+$/ } @_; } sub is_valid_fullname { grep { /\S \S/ } @_; } sub is_valid_emailaddress { grep { !/\s/ && /\S@\S+\.\S+/ } @_; } sub login # works, but oddly enough doesn't authenticate for the adduser page { my $hosturl = 'https://svn.win.tue.nl/'; my $mech = WWW::Mechanize->new( ssl_opts => { verify_hostname => 0, cookie_jar => my $cjar } ); my $res = $mech->get($hosturl); $res = $mech->follow_meta_redirect() // $res; $res->content =~ 'subversion repositories' or aargh('failed to reach svn.win.tue.nl overview page'); $opt{u} or $opt{u} = ask('Username'); $opt{p} or $opt{p} = ask( 'Password', '*' ); $mech->credentials( $opt{u}, $opt{p} ); eval { $mech->follow_link( text_regex => qr/login/i ) && $mech->success() } or aargh('login failed'); ehm_v( 'on logged-in page', $mech->base() ); $mech; } sub go_to_add_user_form { my ($mech) = @_; $mech->credentials( $opt{u}, $opt{p} ); eval { $mech->get('https://svn.win.tue.nl/svnwebadmin/admin/adduser') } or aargh('login on adduser page failed'); eval { $mech->follow_link( url_regex => qr/adduser/ ) } or aargh( 'cannot reach adduser page:', $@ ); $mech->success() or aargh( 'cannot reach adduser page:', $@ ); ehm_v( 'on adduser page', $mech->base() ); my $form = $mech->form_with_fields( 'newusername', 'fullname', 'email', 'repository', 'submit_new_mail' ) or aargh('adduser page looks weird (no form with the expected fields)'); $form; } sub add_user { my ( $mech, $user, $fullname, $email, @reps ) = @_; $mech->set_fields( newusername => $user, fullname => $fullname, email => $email ); select_repositories( $mech, @reps ) or aargh('cannot select repositories to add user to'); ehm_v( 'adding user', $user, "($fullname)", "<$email>" ); ehm_v( 'with rw access to', @reps ) if @reps; $opt{n} or $mech->click_button( name => 'submit_new_mail' ); # ask for the user to be created! $mech->success() or aargh('could not submit the user creation form'); } sub repository_selector { my ($mech) = @_; my ($repository_selector) = $mech->find_all_inputs( name => 'repository' ) or aargh('adduser page looks weird (no repository selector found)'); $repository_selector; } sub repositories { my ($mech) = @_; repository_selector($mech)->possible_values(); } sub select_repositories { my ($mech) = @_; $mech->select( 'repository', [@_] ); my $repvalue = $mech->value('repository'); #warn "repvalue is a ", ref($repvalue), "\n"; # a WWW::Mechanize! #ehm_v('selected repositories:', join(', ', map { "$_=>$value{$_}" } keys %$value)) } sub add_user_to_repository { my ( $mech, $user, $r ) = @_; $mech->credentials( $opt{u}, $opt{p} ); eval { $mech->get('https://svn.win.tue.nl/svnwebadmin/admin/editrepos') } or aargh('login on editrepos page failed'); if ( !grep { $_ eq $r } repositories($mech) ) { ehm("ignoring request to add user $user to nonexistent repository $r"); return; } select_repositories( $mech, $r ) or aargh('cannot select repository to add user to'); #$mech->click_button( value => 'edit' ); # produces a page with a Python stack trace $mech->submit(); $mech->success() or aargh( 'cannot reach editrepo page:', $@ ); ehm_v( 'arrived on editrepo page:', $mech->base ); #ehm_v('content:', $mech->content); my ($userinput) = $mech->find_all_inputs( name => 'accountpattern' ) or aargh('editrepo page looks weird (no username field)'); my ($submit) = $mech->find_all_inputs( name => 'submit_addaccessbymatch', type => 'submit' ) or aargh('editrepo page looks weird (no add button)'); $userinput->value($user); $opt{n} or $mech->click_button( name => 'submit_addaccessbymatch' ); } sub doitall { my $mech = login(); my $form = go_to_add_user_form($mech); my @repositories = repositories($mech); my @r = (); if ( $opt{r} ) { if ( !grep { $_ eq $opt{r} } @repositories ) { ehm( 'ignoring -r, value not found among repositories:', $opt{r} ); } else { push( @r, $opt{r} ); } } if ( !$opt{b} ) { add_user( $mech, $opt{a}, $opt{f}, $opt{m}, @r ); return; } my $csvh = new Text::CSV() or aargh('cannot set up CSV reader'); while ( my $row = $csvh->getline(*STDIN) ) { my ( $user, $fullname, $email, @reps ) = @$row; if ( !defined $email ) { ehm( 'not enough fields on input line, ignored:', join( ',', @$row ) ); } elsif ( !is_valid_username($user) ) { ehm( 'invalid user name on input line, ignored:', join( ',', @$row ) ); } elsif ( !is_valid_fullname($fullname) ) { ehm( 'invalid full name on input line, ignored:', join( ',', @$row ) ); } elsif ( !is_valid_emailaddress($email) ) { ehm( 'invalid email address on input line, ignored:', join( ',', @$row ) ); } else { my @rs = @r; for my $r (@reps) { if ( grep { $_ eq $r } @repositories ) { push( @rs, $r ); } else { ehm( 'nonexistent repository on input line, ignored:', $r ); } } add_user( $mech, $user, $fullname, $email, @rs ); # this won't add a preexisting $user to @rs, we do want that #add_user_to_repository( $mech, $user, $_ ) for @rs; # doesn't work yet, going to the editrepo screen fails go_to_add_user_form($mech); # so we can add another } } }