]>
Commit | Line | Data |
---|---|---|
878d1583 IB |
1 | #!/usr/bin/perl |
2 | use strict; | |
3 | use warnings; | |
4 | ||
5 | use lib $ENV{GL_LIBDIR}; | |
6 | use Gitolite::Rc; | |
7 | use Gitolite::Common; | |
8 | ||
9 | =for usage | |
10 | Please see usage at https://www.immae.eu/docs/forge-logicielle/gitolite.html#inviter-des-collaborateurs | |
11 | =cut | |
12 | ||
13 | usage() if @ARGV and ($ARGV[0] eq '-h' or $ARGV[0] eq '--help'); | |
14 | ||
15 | my $rb = $rc{GL_REPO_BASE}; | |
16 | my $ab = $rc{GL_ADMIN_BASE}; | |
17 | # get to the keydir | |
18 | _chdir("$ab/keydir"); | |
19 | ||
20 | # save arguments for later | |
21 | my $operation = shift || 'list'; | |
22 | my $invitekeyid = shift || ''; | |
23 | $invitekeyid and $invitekeyid !~ /^[-0-9a-z_]+@[-0-9a-z_]+$/i and die "invalid keyid $invitekeyid\n"; | |
24 | my ($invited, $keyid) = split /@/, $invitekeyid; | |
25 | ||
26 | # get the actual userid and keytype | |
27 | my $gl_user = $ENV{GL_USER}; | |
28 | die "This function is reserved for actual users" if $gl_user =~ s/-invite-(.*)$//; | |
29 | ||
30 | # ---- | |
31 | # first collect the keys | |
32 | ||
33 | my ( @invited_keys ); | |
34 | ||
35 | for my $pubkey (`find . -type f -name "*.pub" | sort`) { | |
36 | chomp($pubkey); | |
37 | $pubkey =~ s(^./)(); # artifact of the find command | |
38 | ||
39 | my $user = $pubkey; | |
40 | $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub | |
41 | $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz | |
42 | ||
43 | if ( $user =~ m(^(zzz-marked-for-...-)?$gl_user-invite-) ) { | |
44 | push @invited_keys, $pubkey; | |
45 | } | |
46 | } | |
47 | ||
48 | # ---- | |
49 | # list mode; just do it and exit | |
50 | sub print_keylist { | |
51 | my ( $message, @list ) = @_; | |
52 | return unless @list; | |
53 | print "== $message ==\n"; | |
54 | my $count = 1; | |
55 | for (@list) { | |
56 | my $fp = fingerprint($_); | |
57 | s/(zzz-marked-for-...-)?$gl_user-invite-//g; | |
58 | s/\.pub$//; | |
59 | s(.*/)(); | |
60 | print $count++ . ": $fp : $_\n"; | |
61 | } | |
62 | } | |
63 | if ( $operation eq 'list' ) { | |
64 | print "you have the following invited keys:\n"; | |
65 | print_keylist( "keys for invited persons", @invited_keys ); | |
66 | print "\n\n"; | |
67 | exit; | |
68 | } | |
69 | ||
70 | # ---- | |
71 | # please see docs for details on how a user interacts with this | |
72 | ||
73 | die "valid operations: add, del\n" unless $operation =~ /^(add|del)$/; | |
74 | ||
75 | if ( $operation eq 'add' ) { | |
76 | print STDERR "please supply the new key on STDIN. (I recommend you | |
77 | don't try to do this interactively, but use a pipe)\n"; | |
78 | kf_add( $gl_user, $invited, $keyid, safe_stdin() ); | |
79 | } elsif ( $operation eq 'del' ) { | |
80 | kf_del( $gl_user, $invited, $keyid ); | |
81 | } | |
82 | ||
83 | exit; | |
84 | ||
85 | # ---- | |
86 | ||
87 | # make a temp clone and switch to it | |
88 | our $TEMPDIR; | |
89 | BEGIN { $TEMPDIR = `mktemp -d -t tmp.XXXXXXXXXX`; } | |
90 | END { `rm -rf $TEMPDIR`; } | |
91 | ||
92 | sub cd_temp_clone { | |
93 | chomp($TEMPDIR); | |
94 | hushed_git( "clone", "$rb/gitolite-admin.git", "$TEMPDIR" ); | |
95 | chdir($TEMPDIR); | |
96 | my $hostname = `hostname`; chomp($hostname); | |
97 | hushed_git( "config", "--get", "user.email" ) and hushed_git( "config", "user.email", $ENV{USER} . "@" . $hostname ); | |
98 | hushed_git( "config", "--get", "user.name" ) and hushed_git( "config", "user.name", "$ENV{USER} on $hostname" ); | |
99 | } | |
100 | ||
101 | sub fingerprint { | |
102 | my ($fp, $output) = ssh_fingerprint_file(shift); | |
103 | # Do not print the output of $output to an untrusted destination. | |
104 | die "does not seem to be a valid pubkey\n" unless $fp; | |
105 | return $fp; | |
106 | } | |
107 | ||
108 | sub safe_stdin { | |
109 | # read one line from STDIN | |
110 | my $data; | |
111 | my $ret = read STDIN, $data, 4096; | |
112 | # current pubkeys are approx 400 bytes so we go a little overboard | |
113 | die "could not read pubkey data" . ( defined($ret) ? "" : ": $!" ) . "\n" unless $ret; | |
114 | die "pubkey data seems to have more than one line\n" if $data =~ /\n./; | |
115 | return $data; | |
116 | } | |
117 | ||
118 | sub hushed_git { | |
119 | local (*STDOUT) = \*STDOUT; | |
120 | local (*STDERR) = \*STDERR; | |
121 | open( STDOUT, ">", "/dev/null" ); | |
122 | open( STDERR, ">", "/dev/null" ); | |
123 | system( "git", @_ ); | |
124 | } | |
125 | ||
126 | sub highlander { | |
127 | # there can be only one | |
128 | my ( $keyid, $die_if_empty, @a ) = @_; | |
129 | # too many? | |
130 | if ( @a > 1 ) { | |
131 | print STDERR " | |
132 | more than one key satisfies this condition, and I can't deal with that! | |
133 | The keys are: | |
134 | ||
135 | "; | |
136 | print STDERR "\t" . join( "\n\t", @a ), "\n\n"; | |
137 | exit 1; | |
138 | } | |
139 | # too few? | |
140 | die "no keys with " . ( $keyid || "empty" ) . " keyid found\n" if $die_if_empty and not @a; | |
141 | ||
142 | return @a; | |
143 | } | |
144 | ||
145 | sub kf_add { | |
146 | my ( $gl_user, $invited, $keyid, $keymaterial ) = @_; | |
147 | ||
148 | # add a new "invited" key for $gl_user. | |
149 | cd_temp_clone(); | |
150 | chdir("keydir"); | |
151 | ||
152 | mkdir("invited"); | |
153 | _print( "invited/$gl_user-invite-$invited\@$keyid.pub", $keymaterial ); | |
154 | hushed_git( "add", "." ) and die "git add failed\n"; | |
155 | my $fp = fingerprint("invited/$gl_user-invite-$invited\@$keyid.pub"); | |
156 | hushed_git( "commit", "-m", "invite add $gl_user-invite-$invited\@$keyid ($fp)" ) and die "git commit failed\n"; | |
157 | system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n"; | |
158 | } | |
159 | ||
160 | sub kf_del { | |
161 | my ( $gl_user, $invited, $keyid ) = @_; | |
162 | ||
163 | cd_temp_clone(); | |
164 | chdir("keydir"); | |
165 | ||
166 | my @pk = highlander( $keyid, 1, grep { m(^(.*/)?(zzz-marked-for-...-)?$gl_user-invite-$invited\@$keyid.pub$) } @invited_keys ); | |
167 | ||
168 | my $fp = fingerprint( $pk[0] ); | |
169 | hushed_git( "rm", $pk[0]) and die "git mv failed\n"; | |
170 | hushed_git( "commit", "-m", "invite del $pk[0] ($fp)" ) and die "git commit failed\n"; | |
171 | system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n"; | |
172 | } |