Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement the Audit Feature and Git Pack Parsing #769

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Bonobo.Git.Server.Test/Bonobo.Git.Server.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
<Compile Include="TestCategories.cs" />
<Compile Include="UnitTests\PasswordServiceTest.cs" />
<Compile Include="UnitTests\GitAuthorizeAttributeTest.cs" />
<Compile Include="UnitTests\ReceivePackInspectStreamTest.cs" />
<Compile Include="UnitTests\UserExtensionsTests.cs" />
<Compile Include="UnitTests\UserModelTest.cs" />
</ItemGroup>
Expand All @@ -193,6 +194,32 @@
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="assets\git-receive-packs\flush-only.raw" />
<Content Include="assets\git-receive-packs\delete-at1.raw" />
<Content Include="assets\git-receive-packs\delete-b1.raw" />
<Content Include="assets\git-receive-packs\delete-t1-with-commits-referenced-by-no-branch.raw" />
<Content Include="assets\git-receive-packs\delete-t1.raw" />
<Content Include="assets\git-receive-packs\force-push-amend-initial-commit.raw" />
<Content Include="assets\git-receive-packs\force-push-reset-one-commit.raw" />
<Content Include="assets\git-receive-packs\force-push-two-commits.raw" />
<Content Include="assets\git-receive-packs\push-at1-with-annontation.raw" />
<Content Include="assets\git-receive-packs\push-t1-and-t2.raw" />
<Content Include="assets\git-receive-packs\push-t1-to-t10.raw" />
<Content Include="assets\git-receive-packs\push-t1-with-two-commits.raw" />
<Content Include="assets\git-receive-packs\push-t1.raw" />
<Content Include="assets\git-receive-packs\push-tag-with-211-chars-name.raw" />
<Content Include="assets\git-receive-packs\push-tag-with-6437-chars-annontation.raw" />
<Content Include="assets\git-receive-packs\push-to-master-one-commit-10-empty-files.raw" />
<Content Include="assets\git-receive-packs\push-to-master-one-commit-empty-file.raw" />
<Content Include="assets\git-receive-packs\push-to-feature-branch-one-commit-empty-file.raw" />
<Content Include="assets\git-receive-packs\push-to-master-two-commits-empty-files.raw" />
<Content Include="assets\git-receive-packs\push-to-new-b1-and-b2-with-no-commits.raw" />
<Content Include="assets\git-receive-packs\push-to-new-b1-and-b2-with-one-commit-each.raw" />
<Content Include="assets\git-receive-packs\push-to-new-b1-with-no-own-commits.raw" />
<Content Include="assets\git-receive-packs\push-to-new-b2-with-one-commit.raw" />
<Content Include="assets\git-receive-packs\very-first-push-empty-initial-commit.raw" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
Expand Down
484 changes: 484 additions & 0 deletions Bonobo.Git.Server.Test/UnitTests/ReceivePackInspectStreamTest.cs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
95 changes: 95 additions & 0 deletions Bonobo.Git.Server/Application/Hooks/AfterPushAuditHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Bonobo.Git.Server.Data;
using Bonobo.Git.Server.Security;
using LibGit2Sharp;

namespace Bonobo.Git.Server.Application.Hooks
{
/// <summary>
/// Adds Bonobo user specific identity information to all newly pushed commits by appending a
/// Git note to them.
/// </summary>
public class AfterPushAuditHandler: IAfterGitPushHandler
{
/// <summary>
/// Fallback username if no database user is known for the pushing client.
/// </summary>
private const string EmptyUser = "anonymous";
private const string EmptyEmail = "unknown";

private readonly IRepositoryRepository _repoConfig;
private readonly IMembershipService _bonoboUsers;

public AfterPushAuditHandler(IRepositoryRepository repoConfig, IMembershipService bonoboUsers)
{
if (repoConfig == null) throw new ArgumentNullException(nameof(repoConfig));
if (bonoboUsers == null) throw new ArgumentNullException(nameof(bonoboUsers));

_repoConfig = repoConfig;
_bonoboUsers = bonoboUsers;
}

public void OnBranchCreated(HttpContext httpContext, GitBranchPushData branchData)
{
HandleAnyNewCommits(httpContext, branchData);
}

public void OnBranchModified(HttpContext httpContext, GitBranchPushData branchData, bool isFastForward)
{
HandleAnyNewCommits(httpContext, branchData);
}

private void HandleAnyNewCommits(HttpContext httpContext, GitBranchPushData branchData)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
if (!IsRepoAuditEnabled(branchData.RepositoryName))
return;

string bonoboUserName = HttpContext.Current.User.Username();
AddCommitNotes(bonoboUserName, branchData);
}

private bool IsRepoAuditEnabled(string repositoryName)
{
var repo = _repoConfig.GetRepository(repositoryName);
return repo.AuditPushUser;
}

private void AddCommitNotes(string bonoboUserName, GitBranchPushData branchData)
{
string email = null;
if (string.IsNullOrEmpty(bonoboUserName))
{
bonoboUserName = EmptyUser;
}
else
{
var bonoboUser = _bonoboUsers.GetUserModel(bonoboUserName);
if (bonoboUser != null)
email = bonoboUser.Email;
}

if (string.IsNullOrWhiteSpace(email))
email = EmptyEmail;

foreach (var commit in branchData.AddedCommits)
{
branchData.Repository.Notes.Add(
commit.Id,
bonoboUserName,
new Signature(bonoboUserName, email, DateTimeOffset.Now),
new Signature(bonoboUserName, email, DateTimeOffset.Now),
"pusher");
}
}

public void OnBranchDeleted(HttpContext httpContext, GitBranchPushData branchData) {}

public void OnTagCreated(HttpContext httpContext, GitTagPushData tagData) {}

public void OnTagDeleted(HttpContext httpContext, GitTagPushData tagData) {}
}
}
40 changes: 40 additions & 0 deletions Bonobo.Git.Server/Application/Hooks/GitBranchPushData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using LibGit2Sharp;

namespace Bonobo.Git.Server.Application.Hooks
{
public struct GitBranchPushData
{
public string RepositoryName { get; set; }
public Repository Repository { get; set; }

/// <summary>
/// Full name of the ref, e.g. "refs/heads/master"
/// </summary>
public string RefName { get; set; }

/// <summary>
/// Branch name as it was extracted from <see cref="RefName" />
/// e.g. "master" or "feature/foo"
/// </summary>
public string BranchName { get; set; }

/// <summary>
/// The commit referenced by the branch.
/// </summary>
/// <returns>
/// The 40 characters long SHA1 hash in hex.
/// </returns>
public string ReferenceCommit { get; set; }

/// <summary>
/// Commits which were not previously referenced by the branch.
/// </summary>
/// <returns>
/// All commits of a newly pushed branch (including the commit the
/// branch originated from) or modified commits of an existing branch.
/// <c>null</c> if branch got deleted.
/// </returns>
public IEnumerable<Commit> AddedCommits { get; set; }
}
}
29 changes: 29 additions & 0 deletions Bonobo.Git.Server/Application/Hooks/GitTagPushData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using LibGit2Sharp;

namespace Bonobo.Git.Server.Application.Hooks
{
public struct GitTagPushData
{
public string RepositoryName { get; set; }
public Repository Repository { get; set; }

/// <summary>
/// Full name of the ref, e.g. "refs/tags/v.1.0"
/// </summary>
public string RefName { get; set; }

/// <summary>
/// Tag name as it was extracted from <see cref="RefName" />
/// e.g. "v.1.0" or "foo-bar"
/// </summary>
public string TagName { get; set; }

/// <summary>
/// The commit referenced by the tag.
/// </summary>
/// <returns>
/// The 40 characters long SHA1 hash in hex.
/// </returns>
public string ReferenceCommitSha { get; set; }
}
}
20 changes: 20 additions & 0 deletions Bonobo.Git.Server/Application/Hooks/IAfterGitPushHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Threading.Tasks;
using System.Web;
using LibGit2Sharp;

namespace Bonobo.Git.Server.Application.Hooks
{
public interface IAfterGitPushHandler
{
void OnBranchCreated(HttpContext httpContext, GitBranchPushData branchData);
void OnBranchDeleted(HttpContext httpContext, GitBranchPushData branchData);

/// <param name="httpContext">The current <see cref="HttpContext" />.</param>
/// <param name="branchData">Data related to the updated branch and repository.</param>
/// <param name="isFastForward"><c>true</c> if push was fast forward, <c>false</c> implies force push.</param>
void OnBranchModified(HttpContext httpContext, GitBranchPushData branchData, bool isFastForward);

void OnTagCreated(HttpContext httpContext, GitTagPushData tagData);
void OnTagDeleted(HttpContext httpContext, GitTagPushData tagData);
}
}
27 changes: 9 additions & 18 deletions Bonobo.Git.Server/Bonobo.Git.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AnonymousComparer.cs" />
<Compile Include="Application\Hooks\AfterPushAuditHandler.cs" />
<Compile Include="Application\Hooks\GitBranchPushData.cs" />
<Compile Include="Application\Hooks\GitTagPushData.cs" />
<Compile Include="Application\Hooks\IAfterGitPushHandler.cs" />
<Compile Include="Attributes\AllViewsAttribute.cs" />
<Compile Include="Attributes\FileExtensionsCustom.cs" />
<Compile Include="Attributes\RepositoryNameNormalizerAttribute.cs" />
Expand Down Expand Up @@ -302,21 +306,13 @@
<Compile Include="Data\Update\SqlServer\AddRepositoryLogo.cs" />
<Compile Include="Extensions\LinqExtensions.cs" />
<Compile Include="Extensions\SignatureExtensions.cs" />
<Compile Include="Configuration\AppSettings.cs" />
<Compile Include="Extensions\UserExtensions.cs" />
<Compile Include="Git\GitService\GitServiceResultParser.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Durability\AutoCreateMissingRecoveryDirectories.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Durability\OneFolderRecoveryFilePathBuilder.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Durability\DurableGitServiceResult.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Durability\IRecoveryFilePathBuilder.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Durability\NamedArguments.cs" />
<Compile Include="Git\GitProtocolCommand.cs" />
<Compile Include="Git\GitReceiveCommand.cs" />
<Compile Include="Git\GitRefType.cs" />
<Compile Include="Git\GitService\GitHandlerInvocationService.cs" />
<Compile Include="Git\ReceivePackInspectStream.cs" />
<Compile Include="Git\GitService\GitExecutionResult.cs" />
<Compile Include="Git\GitService\ReceivePackHook\GIT_OBJ_TYPE.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Durability\ReceivePackRecovery.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Hooks\AuditPusherToGitNotes.cs" />
<Compile Include="Git\GitService\ReceivePackHook\ReceivePackCommit.cs" />
<Compile Include="Git\GitService\ReceivePackHook\ReceivePackCommitSignature.cs" />
<Compile Include="Git\GitService\ReplicatingStream.cs" />
<Compile Include="Helpers\ADHelper.cs" />
<Compile Include="Helpers\MembershipHelper.cs" />
<Compile Include="Helpers\RepositoryCommitModelHelpers.cs" />
Expand Down Expand Up @@ -385,11 +381,6 @@
<Compile Include="Attributes\WebAuthorizeAttribute.cs" />
<Compile Include="Git\GitCmdResult.cs" />
<Compile Include="Git\GitService\GitServiceExtensions.cs" />
<Compile Include="Git\GitService\ReceivePackHook\Hooks\NullReceivePackHook.cs" />
<Compile Include="Git\GitService\ReceivePackHook\IHookReceivePack.cs" />
<Compile Include="Git\GitService\ReceivePackHook\ParsedReceivePack.cs" />
<Compile Include="Git\GitService\ReceivePackHook\ReceivePackPktLine.cs" />
<Compile Include="Git\GitService\ReceivePackHook\ReceivePackParser.cs" />
<Compile Include="Git\IGitRepositoryLocator.cs" />
<Compile Include="Helpers\ContentDispositionUtil.cs" />
<Compile Include="Helpers\CssRewriteUrlTransformWrapper.cs" />
Expand Down
19 changes: 0 additions & 19 deletions Bonobo.Git.Server/Configuration/AppSettings.cs

This file was deleted.

12 changes: 12 additions & 0 deletions Bonobo.Git.Server/Git/GitProtocolCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Bonobo.Git.Server.Git
{
/// <summary>
/// Commands which can be represented by git pkt-lines.
/// </summary>
public enum GitProtocolCommand
{
Create,
Delete,
Modify
}
}
67 changes: 67 additions & 0 deletions Bonobo.Git.Server/Git/GitReceiveCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace Bonobo.Git.Server.Git
{
public struct GitReceiveCommand
{
public static GitReceiveCommand Invalid = default(GitReceiveCommand);

/// <summary>
/// Full name of the ref, e.g. "refs/heads/master"
/// </summary>
public string FullRefName { get; set; }

public GitRefType RefType { get; set; }

/// <summary>
/// Branch name as it was extracted from <see cref="RefName" />
/// e.g. "master" or "feature/foo"
/// </summary>
public string RefName { get; set; }

public GitProtocolCommand CommandType { get; set; }

/// <summary>
/// SHA1 identifier of the old object as hex string.
/// </summary>
/// <remarks>
/// 40 characters long SHA1 hash in hex.
/// Will be zero if <c>CommandType</c> is <c>CommandType.Create</c>.
/// </remarks>
public string OldSha1 { get; set; }

/// <summary>
/// SHA1 identifier of the new object as hex string.
/// </summary>
/// <remarks>
/// 40 characters long SHA1 hash in hex.
/// Will be zero if <c>CommandType</c> is <c>CommandType.Delete</c>.
/// </remarks>
public string NewSha1 { get; set; }

public GitReceiveCommand(string fullRefName, string oldSha1, string newSha1)
{
this.OldSha1 = oldSha1;
this.NewSha1 = newSha1;

const string zeroId = "0000000000000000000000000000000000000000";
if (oldSha1 == zeroId)
this.CommandType = GitProtocolCommand.Create;
else if (newSha1 == zeroId)
this.CommandType = GitProtocolCommand.Delete;
else
this.CommandType = GitProtocolCommand.Modify;

this.FullRefName = fullRefName;
int firstSlashPos = fullRefName.IndexOf('/');
int secondSlashPos = fullRefName.IndexOf('/', firstSlashPos + 1);
var refTypeRaw = fullRefName.Substring(firstSlashPos + 1, secondSlashPos - firstSlashPos - 1);
this.RefName = fullRefName.Substring(secondSlashPos + 1);

if (refTypeRaw == "heads")
this.RefType = GitRefType.Branch;
else if (refTypeRaw == "tags")
this.RefType = GitRefType.Tag;
else
this.RefType = GitRefType.Unknown;
}
}
}
Loading