diff options
Diffstat (limited to 'flakes/mypackages/overlays/gitolite')
-rw-r--r-- | flakes/mypackages/overlays/gitolite/default.nix | 8 | ||||
-rwxr-xr-x | flakes/mypackages/overlays/gitolite/invite | 172 |
2 files changed, 180 insertions, 0 deletions
diff --git a/flakes/mypackages/overlays/gitolite/default.nix b/flakes/mypackages/overlays/gitolite/default.nix new file mode 100644 index 0000000..7f8f007 --- /dev/null +++ b/flakes/mypackages/overlays/gitolite/default.nix | |||
@@ -0,0 +1,8 @@ | |||
1 | self: super: { | ||
2 | gitolite = super.gitolite.overrideAttrs(old: { | ||
3 | postPatch = old.postPatch + '' | ||
4 | sed -i -e "s@/bin/rm@rm@" src/commands/sskm | ||
5 | cp ${./invite} src/commands/invite | ||
6 | ''; | ||
7 | }); | ||
8 | } | ||
diff --git a/flakes/mypackages/overlays/gitolite/invite b/flakes/mypackages/overlays/gitolite/invite new file mode 100755 index 0000000..3cc2dbd --- /dev/null +++ b/flakes/mypackages/overlays/gitolite/invite | |||
@@ -0,0 +1,172 @@ | |||
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 | } | ||