aboutsummaryrefslogtreecommitdiff
blob: 30984bfc55e6cbb0ba4a92792a30c89b358c5418 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/perl
use strict;
use warnings;

use lib $ENV{GL_LIBDIR};
use Gitolite::Rc;
use Gitolite::Common;
use Gitolite::Easy;

=for usage
perms -- list or set permissions for user-created ("wild") repo.

Usage summary:
    ssh git@host perms <repo> -l
        # list current permissions on repo
    ssh git@host perms <repo> -lr
        # list available roles and their access rights

    ssh git@host perms <repo> + <rolename> <username>
        # change permissions: add a user to a role
    ssh git@host perms <repo> - <rolename> <username>
        # change permissions: remove a user from a role

Examples:
    ssh git@host perms my/repo + READERS alice
    ssh git@host perms my/repo + WRITERS bob

----
There is also a batch mode useful for scripting and bulk loading.  Do not
combine this with the +/- mode above.  This mode also accepts an optional "-c"
flag to create the repo if it does not already exist (assuming $GL_USER has
permissions to create it).

Examples:
    cat copy-of-backed-up-gl-perms | ssh git@host perms <repo>
    cat copy-of-backed-up-gl-perms | ssh git@host perms -c <repo>
=cut

usage() if not @ARGV or $ARGV[0] eq '-h';

$ENV{GL_USER} or _die "GL_USER not set";

my $generic_error = "repo does not exist, or you are not authorised";

if ( @ARGV >= 2 and $ARGV[1] eq '-l' ) {
    getperms($ARGV[0]);    # doesn't return
}

# auto-create the repo if -c passed and repo doesn't exist
if ( $ARGV[0] eq '-c' ) {
    shift;
    my $repo = $ARGV[0] or usage();
    _die "invalid repo '$repo'" unless $repo =~ $REPONAME_PATT;

    if ( not -d "$rc{GL_REPO_BASE}/$repo.git" ) {
        my $ret = Gitolite::Conf::Load::access( $repo, $ENV{GL_USER}, '^C', 'any' );
        _die $generic_error if $ret =~ /DENIED/;

        require Gitolite::Conf::Store;
        Gitolite::Conf::Store->import;
        new_wild_repo( $repo, $ENV{GL_USER}, 'perms-c' );
        gl_log( 'create', $repo, $ENV{GL_USER}, 'perms-c' );
    }
}

my $repo = shift;

if ( @ARGV and $ARGV[0] eq '-lr' ) {
    list_roles();
    exit 0;
} else {
    setperms(@ARGV);
}

# cache control
if ($rc{CACHE}) {
    require Gitolite::Cache;
    Gitolite::Cache::cache_control('flush', $repo);
}

_system( "gitolite", "trigger", "POST_CREATE", $repo, $ENV{GL_USER}, 'perms' );

# ----------------------------------------------------------------------

sub getperms {
    my $repo = shift;
    _die $generic_error if not owns($repo);
    my $pf = "$rc{GL_REPO_BASE}/$repo.git/gl-perms";

    print slurp($pf) if -f $pf;

    exit 0;
}

sub setperms {
    _die $generic_error if not owns($repo);
    my $pf = "$rc{GL_REPO_BASE}/$repo.git/gl-perms";

    if ( not @_ ) {
        # legacy mode; pipe data in
        print STDERR "'batch' mode started, waiting for input (run with '-h' for details).\n";
        print STDERR "Please enter 'cancel' to abort if you did not intend to do this.\n";
        @ARGV = ();
        my @a;
        while (<>) {
            _die "CANCELLED" if /^\s*cancel\s*$/i;
            invalid_role($1) if /(\S+)/ and not $rc{ROLES}{$1};
            push @a, $_;
        }

        _print( $pf, @a );
        return;
    }

    _die "Invalid syntax.  Please re-run with '-h' for detailed usage" if @_ != 3;
    my ( $op, $role, $user ) = @_;
    _die "Invalid syntax.  Please re-run with '-h' for detailed usage" if $op ne '+' and $op ne '-';
    _die "Invalid user '$user'"                                        if not $user =~ $USERNAME_PATT;

    my $text = '';
    my @text = slurp($pf) if -f $pf;

    my $present = grep { $_ eq "$role $user\n" } @text;

    if ( $op eq '-' ) {
        if ( not $present ) {
            _warn "'$role $user' was not present in file";
        } else {
            @text = grep { $_ ne "$role $user\n" } @text;
            _print( $pf, @text );
        }
    } else {
        invalid_role($role) unless grep { $_->[3] eq $role } load_roles();
        if ($present) {
            _warn "'$role $user' already present in file";
        } else {
            push @text, "$role $user\n";
            @text = sort @text;
            _print( $pf, @text );
        }
    }
}

my @rules;

sub load_roles {
    return @rules if @rules;

    require Gitolite::Conf::Load;
    Gitolite::Conf::Load::load($repo);

    my %repos = %Gitolite::Conf::Load::repos;
    my @repo_memberships = Gitolite::Conf::Load::memberships('repo', $repo);

    for my $rp (@repo_memberships) {
        my $hr = $repos{$rp};
        for my $r ( keys %$hr ) {
            next unless $r =~ s/^@//;
            next unless $rc{ROLES}{$r};
            map { $_->[3] = $r } @{ $hr->{"\@$r"} };
            push @rules, @{ $hr->{"\@$r"} };
        }
    }
    return @rules;
}

sub invalid_role {
    my $role = shift;

    print STDERR "Invalid role '$role'; valid roles for this repo:\n";
    open(STDOUT, '>&', \*STDERR);   # make list_roles print to STDERR
    list_roles();
    exit 1;
}

sub list_roles {

    my @rules = sort { $a->[0] <=> $b->[0] } load_roles();

    for (@rules) {
        $_->[2] =~ s(^refs/heads/)();
        $_->[2] = '--any--' if $_->[2] eq 'refs/.*';
    }

    my $max = 0;
    map { $max = $_ if $_ > $max } map { length($_->[2]) } @rules;
    printf("\t%s\t%*s\t \t%s\n", "perm",  -$max, "ref",   "role");
    printf("\t%s\t%*s\t \t%s\n", "----",  -$max, "---",   "----");
    printf("\t%s\t%*s\t=\t%s\n", $_->[1], -$max, $_->[2], $_->[3]) for @rules;
}