Integrating Gitolite with Atlassian Crowd

There is no argument that git is currently the best SCM tool. Now I can not believe how come we were using CVS,SVN back then and I know that there is no way for me to use any SCM other than git.

While ago we decided to migrate all our repositories to git and while doing that we wanted to revisit the integrations between the other development tools that we are using.

We are currenlty using number of Atlassian tools; Jira, Confluence, Bamboo, Crucible&Fisheye (nearly!! all off them). But the integration between these tools were limited for us. Only Confluence and Jira were aware of each other. I am planning to go over whole setup in detail in another post. Now I would like to focus on one specific scenario an our solution. 10 lines of code over and there really solved our problem. I hope you also benefit from this solution.

Basically what we want is;

  • Being able to see modified files when we look at Jira tickets.
  • Have number of git repositories with a different RW access for different groups.(Btw we are using Git with HTTP protocol)

Sounds easy right, not so much. For the first one, we decided to use Jira – Crucible integration. Make these two applications trust each other and you are good to go, if only all of your repositories are anonymous or if you are using same Identification Service for both Jira and Crucible. We decided to go for the later one (because it is not aligned with our second requirement) and decided to add also Crowd another Atlassian tool to our development tools family.

Our current setting is to use Crowd for all of our Atlassian Tools, define our groups, assign relevant colleagues to groups, make all of our tools trust each other. rucible.

So you may have number of contributers that have an access to your Jira project but only the ones that have right to see the repository configured on Crucible will be able to see the changes on the source.

This setting seems to solve our first problem, for managing git access rights we decided to use gitolite. With gitolite you may create number of groups and assign your git users to them and give different access rights to either users or groups for different repositories.

Creating groups and assigning users to them … creating groups and assigning users to them … Didn’t we already do that on Crowd. We will do that again? Hell no, not at least if I am involved. Here is the 3 step recipe to not to do it again.

STEP 1 : Using Gitolite with Basic Authentication As said previously, we are using Git with HTTP protocol and here is the basic apache configuration to use gitolite authentication in conjunction with basic authentication.

SetEnv GIT_PROJECT_ROOT /var/www/gitolite-home/repositories
SetEnv GIT_HTTP_EXPORT_ALL
SetEnv GITOLITE_HTTP_HOME /var/www/gitolite-home
ScriptAlias  /git /var/www/gitolite-home/bin/gl-auth-command/  
<LocationMatch /git>
  AuthName "Basic"
  AuthType Basic
  AuthUserFile /var/cache/git/passwords
  Require valid-user
</LocationMatch>`

Now an environment variable named “REMOTE_USER” is set and available for gitolite to use.

STEP 2 : Using Gitolite with Crowd

Luckily Crowd has an apache module (mod_crowd) so you can make authentication over Crowd by changing your authentication provider as shown below.

<LocationMatch /git>
   AuthName "Atlassian Crowd"
   AuthType Basic
   AuthBasicProvider crowd
   Options +ExecCGI
   CrowdAppName gitolite
   CrowdAppPassword pass
   CrowdURL http://xxx.xx.xx.xx:8095/crowd/
   Require valid-user
 </LocationMatch>`

Now you are making the authentication over Crowd. Lucky you. But what about the groups. What about the authorization. This is why we need another step.

STEP 3 : Using groups of a user defined in Crowd for gitolite.

Gitolite has built in mechanism to get group definitions from an external source. Quoted from gitolite documentation at github here is how to do that.

All you need is a script that, given a username, queries your LDAP or similar server, and returns a space-separated list of all the groups she is a member of. If an invalid user name is sent in, or the user is valid but is not part of any groups, it should print nothing.

This script will probably be specific to your site. (See contrib/ldap for some example scripts that were contributed by the Nokia MeeGo team.)

Then set the $GL_GET_MEMBERSHIPS_PGM variable in the rc file to the full path of this program, set $GL_BIG_CONFIG to 1, and that will be that.that.

Meaning, with an additional script we can call Crowd via its REST interface and get the groups of a user. But we already visited Crowd once for authentication, and we did not want to do that again. So we made a small change on mod_crowd’s code to also set another environment variable to contain the groups of the user, and our script for gitolite became one line script as shown below.

#!/usr/bin/perl
print $ENV{REMOTE_USER_GROUPS};

The only remaining thing is setting the REMOTE_USER_GROUPS in mod_crowd. To do that we added a new configuration to mod_crowd , so you can give the name of the environment variable that you would like to set. Here is the final Apache configuration.

<LocationMatch /git>
   AuthName "Atlassian Crowd"
   AuthType Basic
   AuthBasicProvider crowd
   Options +ExecCGI
   CrowdAppName gitolite
   CrowdAppPassword pass
   CrowdURL http://xxx.xx.xx.xx:8095/crowd/
   CrowdGroupEnv REMOTE_USER_GROUPS
   Require valid-user
 </LocationMatch>`

And here is the groups_setter method to call from authn_crowd_check_password method to set our beloved environment variable

void groups_setter(request_rec *r) {
    authnz_crowd_dir_config *config = get_config(r);
    char *group_env_name = config->crowd_config->group_env_name;
    if (group_env_name == NULL) {
      return;
    }

    apr_array_header_t *user_groups = authnz_crowd_user_groups(r->user, r);
    if (user_groups == NULL) {
      return ;
    }

    char *user_groups_str =  apr_pcalloc(r->pool,1024 * sizeof(char));
    int y;
    for (y = 0; y < user_groups->nelts; y++) {
      const char *user_group = APR_ARRAY_IDX(user_groups, y, const char *);
      strcat(user_groups_str, user_group);
      if (y+1 < user_groups->nelts) {
        strcat(user_groups_str, " ");
      }
    }

    apr_table_set(r->subprocess_env,group_env_name, user_groups_str);
}

And here is where to get mod_crowd’s source code wget https://studio.plugins.atlassian.com/svn/CWDAPACHE/tags/2.0.2/mod_authnz_crowd-2.0.2.tar.gz

Enjoy!!