# F=ADCs admin defined commands
## ADC background
The admin-defined commands (ADCs) feature allows controlled access to
specific, "safe", programs or scripts, without giving users full shell access.
**WARNING**: regardless of what you read below, the security of the code in
the commands or scripts you install as ADCs is **your responsibility**. The
sample ADCs shipped with gitolite (in `contrib/adc`) should be safe enough,
but an extra pair of eyes never hurt, so please review before use.
Although this is a generic way to allow pretty much any
command to be run, most of the examples and sample ADCs pertain to allowing
users to manage their "own" repos. If that's your use case, please read
the [wildcard repositories][wild] doc before you continue here.
## ADC details
### installing ADCs
ADCs can only be installed by someone with shell access to the server; merely
having push rights to the admin repo is not enough.
* edit `~/.gitolite.rc` and set `$GL_ADC_PATH` to a directory that is *not*
in `$PATH`.
* add your "safe" executables to this directory.
**Warning**: An ADC can hide (or override) gitolite's built-in commands like
'info', 'expand', 'setperms', or even 'git-receive-pack' or 'git-upload-pack'!
This is by design. So be careful what you name your scripts.
However, it is perfectly ok, and may even be necessary in some cases, to name
them after system executables (like 'rsync').
### user invocation
If you have a command called "foo" in that directory, then a user can invoke
it by saying:
ssh git@server foo argument list
### checking authorisation inside an ADC
Once an ADC is installed, *all* users can run it. But sometimes you want only
some people to be able to do so.
While you cannot prevent the ADC from running at all, you can *start* the ADC
with code that checks the user's access to *any* arbitrary repo. For example,
you can bail out if the user does not have "W" access to the "gitolite-admin"
repo, which is an easy way of making sure an ADC is only run by admins.
See the section on "the anatomy of a command" later for this and many more
details.
### checking arguments
Gitolite will call an ADC only if the arguments passed to it match a very
strict pattern (see `$ADC_CMD_ARGS_PATT` in `src/gitolite_rc.pm`). This
reduces the risk of various kinds of shell-meta related compromises.
### passing unchecked arguments
Some commands need arguments with a broader range of characters than
`$ADC_CMD_ARGS_PATT` will allow. As long as you are sure those commands are
doing their own argument checking and sanitisation, you can place such
commands in `$GL_ADC_PATH/ua` and they will be run with **no checks on the
arguments**.
The "ua" stand for "unchecked arguments". Consider this your last warning ;-)
## "fake" repos and access control for non-git programs
A "fake" repo is a repo that exists in the config file but is specially named
(starts with "EXTCMD/") so that gitolite will not create an actual repo on
disk for it. It serves as a place holder for different sets of rules.
If you install the 'rsync' ADC, you can use a fake repo called 'EXTCMD/rsync'
to collect a set of rules that specify what user is allowed to read/write what
files using the rsync command on his workstation. See `contrib/adc/rsync` for
more on this.
*Any* non-git program can be similarly access controlled,
as long as the *command line* that the client attempts to execute on the
server has sufficient information to decide. Protocols where the command line
is just one word and everything else happens in the conversation later cannot
be helped by this mechanism.
## anatomy of a command
You can do whatever you want in an ADC! It's upto you to check the
permissions of *each* repo that the user is manipulating using your ADC --
your code can `rm -rf $GL_REPO_BASE_ABS` if you like and gitolite wouldn't
stop you.
The current directory (`$PWD`) will be set to the `$HOME` of `git@server` (or
whatever id you're using). It won't be any specific repo, it won't even be
the base directory of all the repos.
Gitolite defines a few environment variables, as well as allows you to
directly query the ownership and access permissions of any repository.
The environment variables available are:
* `GL_USER` -- the name of the user invoking the command
* `GL_BINDIR` -- the directory containing all the binaries (in particular,
`gitolite.pm`, which is all we really care about here)
* `GL_REPO_BASE_ABS` -- the absolute path of the base directory containing
all the repos
There are a few other variables also available but the above are the only ones
you should rely on. Please treat any other variables you notice as being
internal/undocumented/subject to change.
[Implementation note: some of the distro packagers don't seem to like
`GL_BINDIR`. I have not tested this in those scenarios, but they probably put
`gitolite.pm` somewhere in perl's lib path anyway, so it ought to work].
You can query ownership and permissions for the current user (which may not
necessarily be the owner). This is done loosely as follows (don't use this
exact code yet though):
perl -I$GL_BINDIR -Mgitolite -e "cli_repo_rights('reponame')"
which will print two space-separated words: permissions and owner. Something
like `_____R__W u1` or maybe `____@R_@W `. (The `u1` indicates the
queried repo is a wildcard repo created by user `u1`; for meanings of the "@"
see doc/report-output.mkd)
But that's cumbersome. There's a bash shell function called
`get_rights_and_owner` in `contrib/adc/adc.common-functions` that is much more
convenient. See any of the other samples for how to use it.
If you prefer perl, there is a nicely commented example in
`contrib/adc/get-rights-and-owner.in-perl`.
## example uses and sample commands in `contrib/adc`
### #fork the 'fork' ADC
A user would use the fork command like this:
ssh git@server fork from to
where "from" is a repo to which the user invoking the fork has "R" access, and
"to" is a repo that does not yet exist and to which he has "C" access.
(Reminder: these access checks are done by the "fork" script, **not** within
gitolite -- once again, you are responsible for making sure your scripts
maintain the security of the system!)
Strictly speaking this command is not really needed. Even without all this
"admin-defined commands" setup you could still do the following, purely from
the client side:
git clone git@server:from
cd from
git remote add new git@server:to
git push new refs/*:refs/*
or some such incantation.
### deleting/trashing repos
See the [repo-deletion document][wild_repodel] for details about this.
### #able enable/disable push access temporarily
If you want to disable push access to gitolite temporarily (maybe for
maintenance), anyone with write access to the gitolite-admin repo can do this:
ssh git@server able dis @all # able dis ==> dis able
To re-enable after the maint work is done:
ssh git@server able en @all # able en ==> en able
You can also do this for one or more individual repos; in place of `@all`,
just use a space separated list of reponames (exactly as they would appear in
the config file). Wildcards are not supported; patches welcome ;-)
Note: please see [this][disable] for more on this.
## how the ADC feature came about
Gitolite was named to be short for "gitosis-lite". Someone now wants to turn
it into a "github-lite" :-) and even had some code to start me off thinking.
Since my first impulse on being asked for a feature is to say no, I was
casting about for a reason when he gave me one: he first made some noises
about perl, then said something about rewriting it all in scheme. Nice... I
resisted the urge to point him to [this][xkcd224], told him that's a great
idea and he should go for it, mentally blessing him for letting me off the
hook on coding it ;-) [Laziness][lazy] *is* the first virtue you know!
[xkcd224]: http://xkcd.com/224/
[lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris
And that was that. For a couple of days.
Soon, though, I realised that there could be a pretty big bonus in this for
tightly controlled setups, so I went and coded it all anyway. See the section
on "restricted admin" for what's really exciting about this for *me*.