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

Added support for Audit Reporting API v2 #29

Open
wants to merge 1 commit 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
227 changes: 227 additions & 0 deletions Egnyte.Api.Tests/Audit/AuditV2ReportTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
using Egnyte.Api.Audit;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;

namespace Egnyte.Api.Tests.Search
{
[TestFixture]
public class AuditV2ReportTests
{
private const string RetryAfter = "20";
private const string RateLimitMinute = "10";
private const string RateLimitRemainingMinute = "0";
private const string RateLimitHour = "100";
private const string RateLimitRemainingHour = "46";

const string GetAuditV2ReportRequestContent = @"
{
""startDate"": ""2021-12-08T00:00:00Z"",
""endDate"": ""2021-12-10T00:00:00Z"",
""auditType"": [
""FILE_AUDIT"",
""USER_AUDIT"",
""GROUP_AUDIT""
]
}";

const string GetAuditV2ReportRequestContentUsingCursor = @"
{
""nextCursor"": ""QmlnVGFibGVLZXk=""
}";

const string GetAuditV2ReportResponseContent = @"
{
""nextCursor"": ""AAN_lwABAX1zZe9AAAAAAAAAAAAAAAAAAAAAAA"",
""events"":
[
{ ""date"":1638936585716,
""sourcePath"":""/Shared/Departments/Marketing/Branding/Logo.jpg"",
""targetPath"":""N/A"",
""user"":""Jack Smith ( [email protected] )"",
""userId"":""101"",
""action"":""Preview"",
""access"":""Web UI"",
""ipAddress"":""173.226.89.189"",
""actionInfo"":"""",
""auditSource"":""FILE_AUDIT""
},
{ ""date"":1638937051697,
""sourcePath"":""/Shared/Departments/Engineering/ProductY/Photo.png"",
""targetPath"":""N/A"",
""user"":""Adam Jackson ( [email protected] )"",
""userId"":""146"",
""action"":""Upload"",
""access"":""Web UI"",
""ipAddress"":""172.8.18.17"",
""actionInfo"":"""",
""auditSource"":""FILE_AUDIT""
},
{ ""date"":1638940605824,
""actor"":""Jennifer Watkins ( [email protected] )"",
""subject"":""Paul Chen ( [email protected] )"",
""action"":""Disable"",
""actionInfo"":"""",
""source"":""Web UI"",
""auditSource"":""USER_AUDIT""
},
{ ""date"":1638942414189,
""actor"":""Jennifer Watkins ( [email protected] )"",
""group"":""Engineering"",
""action"":""Create"",
""actionInfo"":"""",
""source"":""Web UI"",
""auditSource"":""GROUP_AUDIT""
}
],
""moreEvents"":true
}";

[Test]
public async Task GetAuditV2Report_ReturnsSuccess()
{
var httpHandlerMock = new HttpMessageHandlerMock();
var httpClient = new HttpClient(httpHandlerMock);

httpHandlerMock.SendAsyncFunc =
(request, cancellationToken) =>
Task.FromResult(
new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(GetAuditV2ReportResponseContent,
Encoding.UTF8,
"application/json")
});

var egnyteClient = new EgnyteClient("token", "acme", httpClient);
var auditReportResult = await egnyteClient.Audit.GetAuditV2Report(
new DateTime(2021, 12, 08),
new DateTime(2021, 12, 10),
new List<AuditV2Type> { AuditV2Type.FILE_AUDIT, AuditV2Type.USER_AUDIT, AuditV2Type.GROUP_AUDIT });

var requestMessage = httpHandlerMock.GetHttpRequestMessage();
Assert.AreEqual(
"https://acme.egnyte.com/pubapi/v2/audit/stream",
requestMessage.RequestUri.ToString());

Assert.AreEqual(HttpMethod.Post, requestMessage.Method);
Assert.AreEqual("AAN_lwABAX1zZe9AAAAAAAAAAAAAAAAAAAAAAA", auditReportResult.NextCursor);
Assert.AreEqual(4, auditReportResult.Events.Count);

Assert.AreEqual(1638936585716, auditReportResult.Events[0].Date);
Assert.AreEqual("/Shared/Departments/Marketing/Branding/Logo.jpg", auditReportResult.Events[0].SourcePath);
Assert.AreEqual("N/A", auditReportResult.Events[0].TargetPath);
Assert.AreEqual("Jack Smith ( [email protected] )", auditReportResult.Events[0].User);
Assert.AreEqual("101", auditReportResult.Events[0].UserId);
Assert.AreEqual("Preview", auditReportResult.Events[0].Action);
Assert.AreEqual("Web UI", auditReportResult.Events[0].Access);
Assert.AreEqual("173.226.89.189", auditReportResult.Events[0].IpAddress);
Assert.AreEqual("", auditReportResult.Events[0].ActionInfo);
Assert.AreEqual(AuditV2Type.FILE_AUDIT.ToString(), auditReportResult.Events[0].AuditSource);

Assert.AreEqual(true, auditReportResult.MoreEvents);

var requestContent = httpHandlerMock.GetRequestContentAsString();
Assert.AreEqual(
TestsHelper.RemoveWhitespaces(GetAuditV2ReportRequestContent),
TestsHelper.RemoveWhitespaces(requestContent));
}

[Test]
public async Task GetAuditV2Report_WithCursor_ReturnsSuccess()
{
var httpHandlerMock = new HttpMessageHandlerMock();
var httpClient = new HttpClient(httpHandlerMock);

httpHandlerMock.SendAsyncFunc =
(request, cancellationToken) =>
Task.FromResult(
new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(GetAuditV2ReportResponseContent,
Encoding.UTF8,
"application/json")
});

var egnyteClient = new EgnyteClient("token", "acme", httpClient);
var auditReportResult = await egnyteClient.Audit.GetAuditV2Report(
new DateTime(2021, 12, 08),
new DateTime(2021, 12, 10),
new List<AuditV2Type> { AuditV2Type.FILE_AUDIT, AuditV2Type.USER_AUDIT, AuditV2Type.GROUP_AUDIT },
"QmlnVGFibGVLZXk=");

var requestMessage = httpHandlerMock.GetHttpRequestMessage();
Assert.AreEqual(
"https://acme.egnyte.com/pubapi/v2/audit/stream",
requestMessage.RequestUri.ToString());

Assert.AreEqual(HttpMethod.Post, requestMessage.Method);
Assert.AreEqual("AAN_lwABAX1zZe9AAAAAAAAAAAAAAAAAAAAAAA", auditReportResult.NextCursor);
Assert.AreEqual(4, auditReportResult.Events.Count);

Assert.AreEqual(true, auditReportResult.MoreEvents);

var requestContent = httpHandlerMock.GetRequestContentAsString();
Assert.AreEqual(
TestsHelper.RemoveWhitespaces(GetAuditV2ReportRequestContentUsingCursor),
TestsHelper.RemoveWhitespaces(requestContent));
}

[Test]
public async Task GetAuditV2Report_WhenStartDateAndNextCursorAreEmpty_ThrowsException()
{
var httpClient = new HttpClient(new HttpMessageHandlerMock());
var egnyteClient = new EgnyteClient("token", "acme", httpClient);

var exception = await AssertExtensions.ThrowsAsync<ArgumentException>(
() => egnyteClient.Audit.GetAuditV2Report(
auditTypes: new List<AuditV2Type> { AuditV2Type.FILE_AUDIT, AuditV2Type.USER_AUDIT, AuditV2Type.GROUP_AUDIT }));

Assert.IsTrue(exception.Message.Contains("nextCursor"));
Assert.IsNull(exception.InnerException);
}

[Test]
public async Task GetAuditV2Report_ThrowsAuditV2RateLimitExceededException_WhenAccountOverAuditV2Limit()
{
var httpHandlerMock = new HttpMessageHandlerMock();
var httpClient = new HttpClient(httpHandlerMock);
const string Content = "<h1>Developer Over AuditV2 Rate Limit</h1>";
var responseMessage = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)429,
Content = new StringContent(Content)
};
responseMessage.Headers.Add("retry-after", RetryAfter);
responseMessage.Headers.Add("x-ratelimit-limit-minute", RateLimitMinute);
responseMessage.Headers.Add("x-ratelimit-remaining-minute", RateLimitRemainingMinute);
responseMessage.Headers.Add("x-ratelimit-limit-hour", RateLimitHour);
responseMessage.Headers.Add("x-ratelimit-remaining-hour", RateLimitRemainingHour);

httpHandlerMock.SendAsyncFunc =
(request, cancellationToken) =>
Task.FromResult(responseMessage);

var egnyteClient = new EgnyteClient("token", "acme", httpClient);

var exception = await AssertExtensions.ThrowsAsync<AuditV2RateLimitExceededException>(
() => egnyteClient.Audit.GetAuditV2Report(nextCursor: "QmlnVGFibGVLZXk="));

Assert.IsNull(exception.InnerException);
Assert.AreEqual("Audit V2 Report stream over rate limit", exception.Message);

Assert.AreEqual(RetryAfter, exception.RetryAfter);
Assert.AreEqual(RateLimitMinute, exception.RateLimitMinute);
Assert.AreEqual(RateLimitRemainingMinute, exception.RateLimitRemainingMinute);
Assert.AreEqual(RateLimitHour, exception.RateLimitHour);
Assert.AreEqual(RateLimitRemainingHour, exception.RateLimitRemainingHour);
}
}
}
2 changes: 1 addition & 1 deletion Egnyte.Api.Tests/Audit/CreateLoginAuditReportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public async Task CreateLoginAuditReport_ReturnsSuccess()
}

[Test]
public async Task CreateLoginAuditReport_WhenEventsAreEMpty_ThrowsException()
public async Task CreateLoginAuditReport_WhenEventsAreEmpty_ThrowsException()
{
var httpClient = new HttpClient(new HttpMessageHandlerMock());
var egnyteClient = new EgnyteClient("token", "acme", httpClient);
Expand Down
7 changes: 4 additions & 3 deletions Egnyte.Api.Tests/Egnyte.Api.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="System.Text.Json" Version="6.0.8" />
</ItemGroup>

<ItemGroup>
Expand Down
88 changes: 86 additions & 2 deletions Egnyte.Api/Audit/AuditClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,93 @@
public class AuditClient : BaseClient
{
const string AuditReportMethod = "/pubapi/v1/audit";
const string AuditStreamingMethod = "/pubapi/v2/audit/stream";

internal AuditClient(HttpClient httpClient, string domain = "", string host = "") : base(httpClient, domain, host) { }

/// <summary>
/// Access streaming version of audit reporting.
/// </summary>
/// <param name="startDate">Required if nextCursor not specified. Start of date range for the initial set of audit events. The start date should be within the last 7 days (from now). To retrieve past audit events outside of the 7 day window, it is needed to use Audit Reporting API v1.</param>
/// <param name="endDate">Optional. End of date range for the initial set of audit events.</param>
/// <param name="auditTypes">Allows to receive only specific types of audit events. If multiple types of events are required, it is recommended to receive all the required types (specifying the list of types in this filter) and then process them on the client as required. Allows filtering audit events by type based on the list of audit event types.</param>
/// <param name="nextCursor">Iteration pointer for a following (not the initial) request. A cursor is returned in response to the initial request and then every following request generates a new cursor to be used in the next request.</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public async Task<AuditV2ReportResponse> GetAuditV2Report(
DateTime? startDate = null,
DateTime? endDate = null,
List<AuditV2Type> auditTypes = null,
string nextCursor = null)
{
if (startDate == null && nextCursor == null)
{
throw new ArgumentException("Either 'startDate' or 'nextCursor' must be specified.", nameof(nextCursor));
}

var uriBuilder = BuildUri(AuditStreamingMethod);
var httpRequest = new HttpRequestMessage(HttpMethod.Post, uriBuilder.Uri)
{
Content = new StringContent(
GetAuditV2ReportContent(
startDate,
endDate,
auditTypes,
nextCursor),
Encoding.UTF8,
"application/json")
};

var serviceHandler = new ServiceHandler<AuditV2ReportResponse>(httpClient);
var response = await serviceHandler.SendRequestAsync(httpRequest).ConfigureAwait(false);

return response.Data;
}

string GetAuditV2ReportContent(
DateTime? startDate = null,
DateTime? endDate = null,
List<AuditV2Type> auditTypes = null,
string nextCursor = null)
{
var builder = new StringBuilder();

if (nextCursor != null)
{
builder
.Append("{")
.Append(string.Format("\"nextCursor\": \"{0}\"", nextCursor))
.Append("}");
}
else
{
builder
.Append("{")
.Append(string.Format("\"startDate\": \"{0:yyyy-MM-ddTHH:mm:ssZ}\"", startDate));

if (endDate != null)
{
builder
.Append(",")
.Append(string.Format("\"endDate\": \"{0:yyyy-MM-ddTHH:mm:ssZ}\"", endDate));
}

if (auditTypes != null)
{
string auditsContent = "["
+ string.Join(",", auditTypes.Select(e => "\"" + e.ToString() + "\""))
+ "]";
builder
.Append(",")
.Append("\"auditType\": " + auditsContent);
}

builder.Append("}");
}

return builder.ToString();
}

/// <summary>
/// Creates login audit report
/// </summary>
Expand Down Expand Up @@ -44,7 +128,7 @@ public async Task<string> CreateLoginAuditReport(
var httpRequest = new HttpRequestMessage(HttpMethod.Post, uriBuilder.Uri)
{
Content = new StringContent(
GetCrateLoginAuditReportContent(
GetCreateLoginAuditReportContent(
format,
startDate,
endDate,
Expand All @@ -61,7 +145,7 @@ public async Task<string> CreateLoginAuditReport(
return response.Data.Id;
}

string GetCrateLoginAuditReportContent(
string GetCreateLoginAuditReportContent(
AuditReportFormat format,
DateTime startDate,
DateTime endDate,
Expand Down
Loading