Easy guide to mappings between Gerrit Access Control and TeamForge Source Code Permissions

A lot of things have changed since my blog post about Managing git branch level permissions with TeamForge and Gerrit. There are new versions of TeamForge, Gerrit and a new version of our integration is about to be released. One area where changes happened on all three fronts is the mappings between TeamForge permissions and Gerrit access controls. As a result the mapping file has changed. It is nothing to worry about while upgrading. Our integration will perform the transformation of mappings automatically resulting with new mapping file created and old mappings converted to the new format. But it might be worth looking into what has changed and why. In this blog post we will go through those changes to fully understand the old and new format. As a result we will be able to adjust the branch level permissions to fit the new setup.

So let’s talk about changes in mappings between TeamForge and Gerrit.

In the previous version of our integration that was based on Gerrit 2.1.x the mappings file was called gerritforge.mappings and located in /opt/collabnet/gerrit/etc/ directory. This file contained mappings defined as a Java property file (key-value pairs) with possibility to assign a list of properties by adding a numbered suffix to each element. So

property.1=A,v1
property.2=B,v2

is equivalent to

property=A,v1,B,v2

but without need to use comma (“,”) as property separator which, as you see above, could be problematic.

The property names correspond to TeamForge source code permissions clusters: scm_admin (Source Code Admin), scm_delete (Delete/View), scm_view (Comit/View) and none (No Access), which are configurable from TeamForge:
TF source code permissions
The format of mapping file is:

[<name of Git Repo Category>.]<TeamForge SCM permission cluster>.<number of entry>=<Gerrit Access Right Category Code>,<Lower Bound>,<Upper Bound>[,<ref spec>].

For example:

scm_view.1=READ,1,1

is a valid mapping entry for default category (therefore no category name was needed).
Things to note about gerritforge.mappings file:

  • If no ref spec is specified, the default value refs/* will be assumed.
  • If a TeamForge project role has multiple permission clusters, only the most powerful one (scm_admin > scm_delete > scm_commit > scm_view > none) mentioned in the mapping rules file for the corresponding repository category will be considered. If none of the permission clusters of a TeamForge project role are mentioned in the mapping rules file, none will assumed. If none is not mentioned in the mapping rules, no access rights will be assumed.
  • If a Gerrit access right category code is mentioned for a repository category, all previously existing access rights of that access right category will be replaced as long as the [<nameOfGitRepoCategory>.]keep_rights_added_in_gerrit property is set to false. If this property is set to true, existing rights will be kept. The [<nameOfGitRepoCategory>.]keep_rights_added_in_gerrit property also determines whether access rights of Gerrit categories not explicitly mentioned for the repository category will be deleted or kept.

There are 4 repo categories pre-defined in this file: default, optional_review, mandatory_review and custom. Let’s have a brief look at the mappings:

Code Review Policy - Mappings

Note that to use repoCategory feature in TF6.2 it was necessary to define it in Repository description using [RepoCategory:category_name] syntax. This is no longer needed in TF7.0 as shown in the picture below:
TF Edit Repository

Now, let’s have a look at scm_admin permission defined in optional_review category, which is the richest one in term of mappings defined:

optional_review.scm_admin.1=READ,1,3
optional_review.scm_admin.2=CRVW,-2,2
optional_review.scm_admin.3=VRIF,-1,1
optional_review.scm_admin.4=SUBM,1,1
optional_review.scm_admin.5=pHD,1,3
optional_review.scm_admin.6=pTAG,1,2,refs/tags/*
optional_review.scm_admin.7=FORG,1,3
optional_review.scm_admin.8=OWN,1,1

As you see the sections are defined by prefix. Note: no prefix means default.

One parameter which we will have to consider, even if it is not defined with optional_review.scm_admin prefix is:

optional_review.keep_rights_added_in_gerrit=false

This one is defined per category so it applies to all permission clusters.

This format – although initially sufficient – was rather error prone and not easy to maintain in anything but trivial cases. What made things worse, is that the Gerrit part of the mappings has changed significantly between versions 2.1.x and 2.2.x – basically it was rewritten from scratch. Conversion table explaining the changes can be found here.

In the current version of our integration the mapping file is called TeamForgeGerritMappings.xml, and as one could guess from the extension it is an XML file that contains the new mapping format.
So how does the corresponding fragment looks in the new mapping file? Here we go:

 
<RepoCategory name="optional_review" keepRightsAddedInGerrit="false">
 <ScmAdmin>
  <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritViewDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPublishDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritCodeReview upperRange="2" lowerRange="-2" refPattern="refs/*"
   exclusive="false"/>
  <GerritVerify upperRange="1" lowerRange="-1" refPattern="refs/*"
   exclusive="false"/>
  <GerritSubmit value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/*"
   exclusive="false"/>
  <GerritCreateReference value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeCommitterIdentity value="ALLOW" refPattern="refs/*"
   exclusive="false"/>
  <GerritForgeServerIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritOwner value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritAbandon value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritDeleteDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPushMerges value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>
  <GerritPush forcePush="false" value="ALLOW" refPattern="refs/for/refs/*"
   exclusive="false"/>
  <GerritRebase value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>
  <GerritPushAnnotatedTag forcePush="false" value="ALLOW" refPattern="refs/tags/*"
   exclusive="false"/>
  <GerritPushSignedTag value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>
 </ScmAdmin> 
 ... 
</RepoCategory>

After first look one could notice that each access control element has at least three attributes: value, refPattern and exclusive. In all but GerritVerify and GerritCodeReview elements there are no ranges anymore. For two push-related elements – that is GerritPush and GerritPushAnnotatedTag – there is an attribute forcePush defined. Each element has an attribute value=”ALLOW” which is self explanatory and is basically equivalent to +1 in the old config. Value could be also set to DENY or BLOCK. DENY is equivalent to -1 in the old config. So what is BLOCK about? It means that this access right is denied and child projects cannot overwrite this. By configuring a BLOCK permission on the All-Projects you might globally prohibit certain access rights. Then even project owners cannot re-enable this access right by explicitly assigning ALLOW for it (which would be possible if it would just be a DENY on the All-Projects project).

We can also see exclusive=”false” attribute for all entries. This one, when set to true allows to configure Gerrit to grant an exclusive ref level access control so that only users of a specific group can perform an operation on a project/reference pair. In version 2.1.7 one could enable this option by prefixing the reference specified with a minus (‘-‘). ← This one looks funny.

Next thing is that the parameter keepRightsAddedInGerrit which originates from:

optional_review.keep_rights_added_in_gerrit=false

is, as expected, defined on repo category level (as an attribute of RepoCategory element).

Another obvious thing to notice is that there are much more entries, actually there are 19 access control elements in the new file as opposed to 8 properties in the old one. How is this possible? Well, some entries are a result of transforming ranges of single access categories to separate access categories (for example Forge +1, +2 and +3 correspond to three new access categories, and READ +1, +2 and +3 even to six) some other (like GerritPublishDraft, GerritViewDraft, GerritDeleteDraft and GerritRebase) are there due to the new functionality of Gerrit.

Let’s analyse our xml file starting from the top and explain line by line.

The first one is:

<GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>

this is an easy one as it comes from:

optional_review.scm_admin.1=READ,1,3

But it is not the only line derived from this line:
read_1_3

As you can see in the picture above READ 1,3 (Upload Merge Access) implies READ 1,1 (Read access) and READ 1,2 (Upload access). But READ 1,2 and READ 1,3 are no longer used in Gerrit 2.6. In fact In Gerrit 2.2.x, the way to set permissions for upload has changed entirely. To upload a change for review is no longer a separate permission type, instead you grant ordinary push permissions to the actual receiving reference. In practice this means that you set push permissions on refs/for/refs/heads/<branch> rather than permissions to upload changes on refs/heads/<branch>.

Next new element is rebase permission:

<GerritRebase value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>

It is related to the fact that before Gerrit 2.4 only change owner and submitters were permitted to rebase a change in the web UI by pushing the ‘Rebase Change’ button. Gerrit 2.4 introduces a new access right that can be assigned to other users to permit them to do the rebase in the web UI as well.
Finally we see two Drafts related permissions:

<GerritViewDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/> 
<GerritPublishDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>

What are those two drafts-related permission about? Well, drafts are basically changes which are not yet intended for review (you could think about it as something with before-NEW status). So those two permissions allow the admin user to view and delete drafts. Note that drafts permission were not available in Gerrit 2.1.x, as this feature was introduced in Gerrit 2.3.

To summarize READ 1,3 corresponds to the following lines:

<GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
<GerritViewDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>
<GerritPushMerges value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>
<GerritPush forcePush="false" value="ALLOW" refPattern="refs/for/refs/*" 
 exclusive="false"/>
<GerritPublishDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>
<GerritRebase value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>

Looking in the next line we find something Gerrit 2.1.x users should be familiar with:

<GerritCodeReview upperRange="2" lowerRange="-2" refPattern="refs/*" 
 exclusive="false"/>

Indeed it corresponds to the line 2 of the old mappings:

optional_review.scm_admin.2=CRVW,-2,2

The next two lines are straightforward too:

<GerritVerify upperRange="1" lowerRange="-1" refPattern="refs/*" 
 exclusive="false"/>
<GerritSubmit value="ALLOW" refPattern="refs/*" exclusive="false"/>

as it comes from lines 3 and 4 of the old mapping file:

optional_review.scm_admin.3=VRIF,-1,1
optional_review.scm_admin.4=SUBM,1,1

The next two elements are more interesting:

<GerritPush forcePush="true" value="ALLOW" refPattern="refs/*" 
 exclusive="false"/>
<GerritCreateReference value="ALLOW" refPattern="refs/*" exclusive="false"/>

both are related to line 5 of the old config:

optional_review.scm_admin.5=pHD,1,3

pHD 1 3
We end up with only two options instead of three as the pHD 1 differs from pHD 3 only by forcePush parameter set to false, so it already covered.

Now we have reached the elements ForgeAuthor, ForgeCommiter
and ForgeServer that are related to different forge permissions:

<GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
<GerritForgeCommitterIdentity value="ALLOW" refPattern="refs/*"
 exclusive="false"/>
<GerritForgeServerIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>

those are related to line 7 from the old config file:

optional_review.scm_admin.7=FORG,1,3

The screenshot below explains how those three are derived from line 7 of the old mappings file:
FORGE 1 3
Back to our XML document, Gerrit Owner in the next line is another easy one:

 <GerritOwner value="ALLOW" refPattern="refs/*" exclusive="false"/>

as it comes directly from line 8:

optional_review.scm_admin.8=OWN,1,1

But, as shown in the picture below it is not the only one which comes from line 8:
OWN 1 1
Two new elements here are GerritAbandon and GerritDeleteDrafts. Let’s have a look at them. Starting from abandon:

 <GerritAbandon value="ALLOW" refPattern="refs/*" exclusive="false"/>

What is Gerrit Abandon category about? Let’s have a look into Gerrit documentation:

This category controls whether users are allowed to abandon changes to projects in Gerrit. It can give permission to abandon a specific change to a given ref. This also grants the permission to restore a change if the change can be uploaded.

We have already talked about Drafts permissions. As name indicates GerritDeleteDrafts allows user to delete any drafts:

<GerritDeleteDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>

So let’s put together all permissions which come from line 8 of our config:

<GerritOwner value="ALLOW" refPattern="refs/*" exclusive="false"/>
<GerritAbandon value="ALLOW" refPattern="refs/*" exclusive="false"/>
<GerritDeleteDrafts value="ALLOW" refPattern="refs/*" exclusive="false"/>

At this point we are almost done, especially that we can safely skip next three lines:

<GerritPushMerges value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>
<GerritPush forcePush="false" value="ALLOW" refPattern="refs/for/refs/*" 
  exclusive="false"/>
<GerritRebase value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>

as it is a result of READ,1,3 as described earlier in this blog.
The last two lines left are about push tags categories:

<GerritPushAnnotatedTag forcePush="false" value="ALLOW" refPattern="refs/tags/*" 
  exclusive="false"/>
<GerritPushSignedTag value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>

Those were derived from line 6:

optional_review.scm_admin.6=pTAG,1,2,refs/tags/*

PushSignedTag comes from pTag,1,1 and PushAnnotatedTag from pTag,1,2.
So, we are finally done with the scm_admin section. But if you look again into the mapping definition xsd file you will see that there are two entries not covered by our mappings at all:

  • GerritRemoveReviewer – which permits users to remove other users from the list of reviewers on a change
  • GerritEditTopicName – which permits users to edit the topic name of a change that is uploaded for review.

Those two elements are not necessary in our mappings for scm_admin because change owner, project owner and site administrator can always remove reviewers and edit topic names.

As you can see the access control options of Gerrit are quite complicated. So, good to know that TeamForge hides this complexity behind relatively simple repo categories which, with combination with access rights, lets you to control your Gerrit in much simpler way. However, if you want to have more sophisticated setup (like for example branch level permissions) it is crucial for you to understand the mappings, to be able to configure Gerrit with TeamForge according to your needs. What’s your experience on maintaining Gerrit access control with TeamForge? Do you have any questions related to TeamForge source code permissions, Gerrit access control categories or mappings between them? Any feedback on this article? Please, feel free to contact me to discuss any of those topics.

Resources:

[1] TeamForge
[2] TeamForge Documentation
[3] TeamForge Git/Gerrit Integration
[4] Documentation
[5] Gerrit Access Control

Eryk Szymanski

Eryk is CollabNet’s Development Manager leading Git and Gerrit related development efforts. He has over 20 years of engineering and management experience ranging from start-ups to medium-size enterprises. Eryk holds Master degree in Computer Science and is Certified Scrum Master.

Tagged with: , ,
Posted in Git, TeamForge

Leave a Reply

Your email address will not be published. Required fields are marked *

*