groovy/protect-refs.groovy | ●●●●● patch | view | raw | blame | history | |
tests/com/gitblit/tests/GroovyScriptTest.java | ●●●●● patch | view | raw | blame | history |
groovy/protect-refs.groovy
New file @@ -0,0 +1,108 @@ /* * Copyright 2012 Philip L. McMahon. * * Derived from blockpush.groovy, copyright 2011 gitblit.com. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.gitblit.GitBlit import com.gitblit.models.RepositoryModel import com.gitblit.models.UserModel import org.eclipse.jgit.transport.ReceiveCommand import org.eclipse.jgit.transport.ReceiveCommand.Result import org.slf4j.Logger /** * Sample Gitblit Pre-Receive Hook: protect-refs * * This script provides basic authorization of receive command types for a list * of known ref patterns. Command types and unmatched ref patterns will be * ignored, meaning this script has an "allow by default" policy. * * This script works best when a repository requires authentication on push, but * can be used to enforce fast-forward commits or prohibit ref deletion by * setting the *authorizedTeams* variable to an empty list and adding a ".+" * entry to the *protectedRefs* list. * * The Pre-Receive hook is executed after an incoming push has been parsed, * validated, and objects have been written but BEFORE the refs are updated. * This is the appropriate point to block a push for some reason. * * This script is only executed when pushing to *Gitblit*, not to other Git * tooling you may be using. * * If this script is specified in *groovy.preReceiveScripts* of gitblit.properties * or web.xml then it will be executed by any repository when it receives a * push. If you choose to share your script then you may have to consider * tailoring control-flow based on repository access restrictions. * * Scripts may also be specified per-repository in the repository settings page. * Shared scripts will be excluded from this list of available scripts. * * This script is dynamically reloaded and it is executed within it's own * exception handler so it will not crash another script nor crash Gitblit. * * This script may reject one or more commands, but will never return false. * Subsequent scripts, if any, will always be invoked. * * Bound Variables: * gitblit Gitblit Server com.gitblit.GitBlit * repository Gitblit Repository com.gitblit.models.RepositoryModel * user Gitblit User com.gitblit.models.UserModel * commands JGit commands Collection<org.eclipse.jgit.transport.ReceiveCommand> * url Base url for Gitblit String * logger Logger instance org.slf4j.Logger * */ // map of protected command types to returned results type // commands not included will skip authz check def protectedCmds = [ UPDATE_NONFASTFORWARD: Result.REJECTED_NONFASTFORWARD, DELETE: Result.REJECTED_NODELETE ] // list of regex patterns for protected refs def protectedRefs = [ "refs/heads/master", "refs/tags/.+" ] // teams which are authorized to perform protected commands on protected refs def authorizedTeams = [ "admins" ] for (ReceiveCommand command : commands) { def updateType = command.type def updatedRef = command.refName // find first regex which matches updated ref, if any def refPattern = protectedRefs.find { updatedRef.matches ~it } // find rejection result for update type, if any def result = protectedCmds[updateType.name()] // command requires authz if ref is protected and has a mapped rejection result if (refPattern && result) { // verify user is a member of any authorized team def team = authorizedTeams.find { user.isTeamMember it } if (team) { // don't adjust command result logger.info "${user.username} authorized for ${updateType} of protected ref ${repository.name}:${updatedRef} (${command.oldId.name} -> ${command.newId.name})" } else { // mark command result as rejected command.setResult(result, "${user.username} cannot ${updateType} protected ref ${repository.name}:${updatedRef} matching pattern ${refPattern}") } } } tests/com/gitblit/tests/GroovyScriptTest.java
@@ -89,6 +89,97 @@ } @Test public void testProtectRefsCreateBranch() throws Exception { MockGitblit gitblit = new MockGitblit(); MockLogger logger = new MockLogger(); List<ReceiveCommand> commands = new ArrayList<ReceiveCommand>(); commands.add(new ReceiveCommand(ObjectId.zeroId(), ObjectId .fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), "refs/heads/master")); RepositoryModel repository = new RepositoryModel("ex@mple.git", "", "admin", new Date()); test("protect-refs.groovy", gitblit, logger, commands, repository); } @Test public void testProtectRefsCreateTag() throws Exception { MockGitblit gitblit = new MockGitblit(); MockLogger logger = new MockLogger(); List<ReceiveCommand> commands = new ArrayList<ReceiveCommand>(); commands.add(new ReceiveCommand(ObjectId.zeroId(), ObjectId .fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), "refs/tags/v1.0")); RepositoryModel repository = new RepositoryModel("ex@mple.git", "", "admin", new Date()); test("protect-refs.groovy", gitblit, logger, commands, repository); assertEquals(0, logger.messages.size()); } @Test public void testProtectRefsFastForward() throws Exception { MockGitblit gitblit = new MockGitblit(); MockLogger logger = new MockLogger(); List<ReceiveCommand> commands = new ArrayList<ReceiveCommand>(); commands.add(new ReceiveCommand(ObjectId .fromString("c18877690322dfc6ae3e37bb7f7085a24e94e887"), ObjectId .fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), "refs/heads/master")); RepositoryModel repository = new RepositoryModel("ex@mple.git", "", "admin", new Date()); test("protect-refs.groovy", gitblit, logger, commands, repository); assertEquals(0, logger.messages.size()); } @Test public void testProtectRefsDeleteMasterBranch() throws Exception { MockGitblit gitblit = new MockGitblit(); MockLogger logger = new MockLogger(); List<ReceiveCommand> commands = new ArrayList<ReceiveCommand>(); ReceiveCommand command = new ReceiveCommand(ObjectId .fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), ObjectId.zeroId(), "refs/heads/master"); commands.add(command); RepositoryModel repository = new RepositoryModel("ex@mple.git", "", "admin", new Date()); test("protect-refs.groovy", gitblit, logger, commands, repository); assertEquals(ReceiveCommand.Result.REJECTED_NODELETE, command.getResult()); assertEquals(0, logger.messages.size()); } @Test public void testProtectRefsDeleteOtherBranch() throws Exception { MockGitblit gitblit = new MockGitblit(); MockLogger logger = new MockLogger(); List<ReceiveCommand> commands = new ArrayList<ReceiveCommand>(); commands.add(new ReceiveCommand(ObjectId .fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), ObjectId.zeroId(), "refs/heads/other")); RepositoryModel repository = new RepositoryModel("ex@mple.git", "", "admin", new Date()); test("protect-refs.groovy", gitblit, logger, commands, repository); assertEquals(0, logger.messages.size()); } @Test public void testProtectRefsDeleteTag() throws Exception { MockGitblit gitblit = new MockGitblit(); MockLogger logger = new MockLogger(); List<ReceiveCommand> commands = new ArrayList<ReceiveCommand>(); ReceiveCommand command = new ReceiveCommand(ObjectId .fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), ObjectId.zeroId(), "refs/tags/v1.0"); commands.add(command); RepositoryModel repository = new RepositoryModel("ex@mple.git", "", "admin", new Date()); test("protect-refs.groovy", gitblit, logger, commands, repository); assertEquals(ReceiveCommand.Result.REJECTED_NODELETE, command.getResult()); assertEquals(0, logger.messages.size()); } @Test public void testBlockPush() throws Exception { MockGitblit gitblit = new MockGitblit(); MockLogger logger = new MockLogger();