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

Add auditing for all user actions #3078 #3083

Merged
merged 1 commit into from
Jun 22, 2016
Merged
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,5 @@ src/NuGetGallery.Cloud/ecf/
*.trx

# Vs2015
.vs/config/applicationhost.config
.vs/config/applicationhost.config
src/NuGetGallery/App_Data/Files/auditing/
4 changes: 3 additions & 1 deletion src/NuGetGallery.Core/Auditing/AuditActor.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace NuGetGallery.Auditing
Expand All @@ -23,9 +23,11 @@ public class AuditActor

public AuditActor(string machineName, string machineIP, string userName, string authenticationType, DateTime timeStampUtc)
: this(machineName, machineIP, userName, authenticationType, timeStampUtc, null) { }

public AuditActor(string machineName, string machineIP, string userName, string authenticationType, DateTime timeStampUtc, AuditActor onBehalfOf)
{
MachineName = machineName;
MachineIP = machineIP;
UserName = userName;
AuthenticationType = authenticationType;
TimestampUtc = timeStampUtc;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGetGallery.Auditing
{
public enum AuditedAuthenticatedOperationAction
{
/// <summary>
/// Package push was attempted by a non-owner of the package
/// </summary>
PackagePushAttemptByNonOwner,

/// <summary>
/// Login failed, no such user
/// </summary>
FailedLoginNoSuchUser,

/// <summary>
/// Login failed, user exists but password is invalid
/// </summary>
FailedLoginInvalidPassword,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace NuGetGallery.Auditing.AuditedEntities
{
public class AuditedPackage
{
public int PackageRegistrationKey { get; private set; }
public string Copyright { get; private set; }
public DateTime Created { get; private set; }
public string Description { get; private set; }
public string ReleaseNotes { get; private set; }
public int DownloadCount { get; private set; }
public string ExternalPackageUrl { get; private set; }
public string HashAlgorithm { get; private set; }
public string Hash { get; private set; }
public string IconUrl { get; private set; }
public bool IsLatest { get; private set; }
public bool IsLatestStable { get; private set; }
public DateTime LastUpdated { get; private set; }
public DateTime? LastEdited { get; private set; }
public string LicenseUrl { get; private set; }
public bool HideLicenseReport { get; private set; }
public string Language { get; private set; }
public DateTime Published { get; private set; }
public long PackageFileSize { get; private set; }
public string ProjectUrl { get; private set; }
public bool RequiresLicenseAcceptance { get; private set; }
public string Summary { get; private set; }
public string Tags { get; private set; }
public string Title { get; private set; }
public string Version { get; private set; }
public string NormalizedVersion { get; private set; }
public string LicenseNames { get; private set; }
public string LicenseReportUrl { get; private set; }
public bool Listed { get; private set; }
public bool IsPrerelease { get; private set; }
public string FlattenedAuthors { get; private set; }
public string FlattenedDependencies { get; private set; }
public int Key { get; private set; }
public string MinClientVersion { get; private set; }
public int? UserKey { get; private set; }
public bool Deleted { get; private set; }

public static AuditedPackage CreateFrom(Package package)
{
return new AuditedPackage
{
PackageRegistrationKey = package.PackageRegistrationKey,
Copyright = package.Copyright,
Created = package.Created,
Description = package.Description,
ReleaseNotes = package.ReleaseNotes,
DownloadCount = package.DownloadCount,
#pragma warning disable 612
#pragma warning restore 612
HashAlgorithm = package.HashAlgorithm,
Hash = package.Hash,
IconUrl = package.IconUrl,
IsLatest = package.IsLatest,
IsLatestStable = package.IsLatestStable,
LastUpdated = package.LastUpdated,
LastEdited = package.LastEdited,
LicenseUrl = package.LicenseUrl,
HideLicenseReport = package.HideLicenseReport,
Language = package.Language,
Published = package.Published,
PackageFileSize = package.PackageFileSize,
ProjectUrl = package.ProjectUrl,
RequiresLicenseAcceptance = package.RequiresLicenseAcceptance,
Summary = package.Summary,
Tags = package.Tags,
Title = package.Title,
Version = package.Version,
NormalizedVersion = package.NormalizedVersion,
LicenseNames = package.LicenseNames,
LicenseReportUrl = package.LicenseReportUrl,
Listed = package.Listed,
IsPrerelease = package.IsPrerelease,
FlattenedAuthors = package.FlattenedAuthors,
FlattenedDependencies = package.FlattenedDependencies,
Key = package.Key,
MinClientVersion = package.MinClientVersion,
UserKey = package.UserKey,
Deleted = package.Deleted
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGetGallery.Auditing.AuditedEntities
{
public class AuditedPackageIdentifier
{
public string Id { get; }
public string Version { get; }

public AuditedPackageIdentifier(string id, string version)
{
Id = id;
Version = version;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGetGallery.Auditing.AuditedEntities
{
public class AuditedPackageRegistration
{
public string Id { get; private set; }
public int DownloadCount { get; private set; }
public int Key { get; private set; }

public static AuditedPackageRegistration CreateFrom(PackageRegistration packageRegistration)
{
return new AuditedPackageRegistration
{
Id = packageRegistration.Id,
DownloadCount = packageRegistration.DownloadCount,
Key = packageRegistration.Key
};
}
}
}
18 changes: 18 additions & 0 deletions src/NuGetGallery.Core/Auditing/AuditedPackageAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGetGallery.Auditing
{
public enum AuditedPackageAction
{
Delete,
SoftDelete,
Create,
List,
Unlist,
Edit,
UndoEdit,


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGetGallery.Auditing
{
public enum AuditedPackageRegistrationAction
{
AddOwner,
RemoveOwner
}
}
17 changes: 17 additions & 0 deletions src/NuGetGallery.Core/Auditing/AuditedUserAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGetGallery.Auditing
{
public enum AuditedUserAction
{
Register,
AddCredential,
RemoveCredential,
RequestPasswordReset,
ChangeEmail,
CancelChangeEmail,
ConfirmEmail,
Login
}
}
12 changes: 5 additions & 7 deletions src/NuGetGallery.Core/Auditing/AuditingService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
Expand All @@ -15,7 +12,7 @@ public abstract class AuditingService
{
public static readonly AuditingService None = new NullAuditingService();

private static readonly JsonSerializerSettings _auditRecordSerializerSettings;
private static readonly JsonSerializerSettings AuditRecordSerializerSettings;

static AuditingService()
{
Expand All @@ -31,7 +28,7 @@ static AuditingService()
TypeNameHandling = TypeNameHandling.None
};
settings.Converters.Add(new StringEnumConverter());
_auditRecordSerializerSettings = settings;
AuditRecordSerializerSettings = settings;
}

public virtual async Task<Uri> SaveAuditRecord(AuditRecord record)
Expand All @@ -48,7 +45,7 @@ public virtual async Task<Uri> SaveAuditRecord(AuditRecord record)

public virtual string RenderAuditEntry(AuditEntry entry)
{
return JsonConvert.SerializeObject(entry, _auditRecordSerializerSettings);
return JsonConvert.SerializeObject(entry, AuditRecordSerializerSettings);
}

/// <summary>
Expand All @@ -73,6 +70,7 @@ protected override Task<Uri> SaveAuditRecord(string auditData, string resourceTy
{
var uriString = $"http://auditing.local/{resourceType}/{filePath}/{timestamp:s}-{action.ToLowerInvariant()}";
var uri = new Uri(uriString);

return Task.FromResult(uri);
}
}
Expand Down
38 changes: 21 additions & 17 deletions src/NuGetGallery.Core/Auditing/CloudAuditingService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.WindowsAzure.Storage;
Expand All @@ -24,23 +22,23 @@ public class CloudAuditingService : AuditingService
private CloudBlobContainer _auditContainer;
private string _instanceId;
private string _localIP;
private Func<Task<AuditActor>> _onBehalfOfThunk;
private Func<Task<AuditActor>> _getOnBehalfOf;

public CloudAuditingService(string instanceId, string localIP, string storageConnectionString, Func<Task<AuditActor>> onBehalfOfThunk)
: this(instanceId, localIP, GetContainer(storageConnectionString), onBehalfOfThunk)
public CloudAuditingService(string instanceId, string localIP, string storageConnectionString, Func<Task<AuditActor>> getOnBehalfOf)
: this(instanceId, localIP, GetContainer(storageConnectionString), getOnBehalfOf)
{

}

public CloudAuditingService(string instanceId, string localIP, CloudBlobContainer auditContainer, Func<Task<AuditActor>> onBehalfOfThunk)
public CloudAuditingService(string instanceId, string localIP, CloudBlobContainer auditContainer, Func<Task<AuditActor>> getOnBehalfOf)
{
_instanceId = instanceId;
_localIP = localIP;
_auditContainer = auditContainer;
_onBehalfOfThunk = onBehalfOfThunk;
_getOnBehalfOf = getOnBehalfOf;
}

public static Task<AuditActor> AspNetActorThunk()
public static Task<AuditActor> GetAspNetOnBehalfOf()
{
// Use HttpContext to build an actor representing the user performing the action
var context = HttpContext.Current;
Expand All @@ -50,14 +48,20 @@ public static Task<AuditActor> AspNetActorThunk()
}

// Try to identify the client IP using various server variables
string clientIP = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (String.IsNullOrEmpty(clientIP)) // Try REMOTE_ADDR server variable
string clientIpAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrEmpty(clientIpAddress)) // Try REMOTE_ADDR server variable
{
clientIP = context.Request.ServerVariables["REMOTE_ADDR"];
clientIpAddress = context.Request.ServerVariables["REMOTE_ADDR"];
}
if (String.IsNullOrEmpty(clientIP)) // Try UserHostAddress property

if (string.IsNullOrEmpty(clientIpAddress)) // Try UserHostAddress property
{
clientIpAddress = context.Request.UserHostAddress;
}

if (!string.IsNullOrEmpty(clientIpAddress) && clientIpAddress.IndexOf(".", StringComparison.Ordinal) > 0)
{
clientIP = context.Request.UserHostAddress;
clientIpAddress = clientIpAddress.Substring(0, clientIpAddress.LastIndexOf(".", StringComparison.Ordinal)) + ".0";
}

string user = null;
Expand All @@ -70,7 +74,7 @@ public static Task<AuditActor> AspNetActorThunk()

return Task.FromResult(new AuditActor(
null,
clientIP,
clientIpAddress,
user,
authType,
DateTime.UtcNow));
Expand All @@ -80,8 +84,8 @@ protected override async Task<AuditActor> GetActor()
{
// Construct an actor representing the user the service is acting on behalf of
AuditActor onBehalfOf = null;
if(_onBehalfOfThunk != null) {
onBehalfOf = await _onBehalfOfThunk();
if(_getOnBehalfOf != null) {
onBehalfOf = await _getOnBehalfOf();
}
return await AuditActor.GetCurrentMachineActor(onBehalfOf);
}
Expand Down
Loading