Skip to content

Commit

Permalink
feat(api): add Security Logs API support
Browse files Browse the repository at this point in the history
  • Loading branch information
innomaxx committed Feb 1, 2024
1 parent 486e863 commit c378589
Show file tree
Hide file tree
Showing 9 changed files with 555 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/Crowdin.Api/Core/InternalExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ internal static void AddParamIfPresent(this IDictionary<string, string> queryPar
queryParams.AddParamIfPresent(key, value.ToString());
}
}

internal static void AddParamIfPresent(this IDictionary<string, string> queryParams, string key, long? value)
{
if (value.HasValue)
{
queryParams.AddParamIfPresent(key, value.ToString());
}
}

internal static void AddParamIfPresent(this IDictionary<string, string> queryParams, string key, object? value)
{
Expand Down
35 changes: 35 additions & 0 deletions src/Crowdin.Api/SecurityLogs/SecurityLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

using System;
using JetBrains.Annotations;
using Newtonsoft.Json;

namespace Crowdin.Api.SecurityLogs
{
[PublicAPI]
public class SecurityLog
{
[JsonProperty("id")]
public long Id { get; set; }

[JsonProperty("event")]
public SecurityLogEventType Event { get; set; }

[JsonProperty("info")]
public string Info { get; set; }

[JsonProperty("userId")]
public long UserId { get; set; }

[JsonProperty("location")]
public string Location { get; set; }

[JsonProperty("ipAddress")]
public string IpAddress { get; set; }

[JsonProperty("deviceName")]
public string DeviceName { get; set; }

[JsonProperty("createdAt")]
public DateTimeOffset CreatedAt { get; set; }
}
}
76 changes: 76 additions & 0 deletions src/Crowdin.Api/SecurityLogs/SecurityLogEventType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

using System.ComponentModel;
using JetBrains.Annotations;

namespace Crowdin.Api.SecurityLogs
{
[PublicAPI]
public enum SecurityLogEventType
{
[Description("login")]
Login,

[Description("password.set")]
PasswordSet,

[Description("password.change")]
PasswordChange,

[Description("email.change")]
EmailChange,

[Description("login.change")]
LoginChange,

[Description("personal_token.issued")]
PersonalTokenIssued,

[Description("personal_token.revoked")]
PersonalTokenRevoked,

[Description("mfa.enabled")]
MfaEnabled,

[Description("mfa.disabled")]
MfaDisabled,

[Description("session.revoke")]
SessionRevoke,

[Description("session.revoke_all")]
SessionRevokeAll,

[Description("sso.connect")]
SsoConnect,

[Description("sso.disconnect")]
SsoDisconnect,

[Description("user.remove")]
UserRemove,

[Description("application.connected")]
ApplicationConnected,

[Description("application.disconnected")]
ApplicationDisconnected,

[Description("webauthn.created")]
WebAuthNCreated,

[Description("webauthn.deleted")]
WebAuthNDeleted,

[Description("trusted_device.remove")]
TrustedDeviceRemove,

[Description("trusted_device.remove_all")]
TrustedDeviceRemoveAll,

[Description("device_verification.enabled")]
DeviceVerificationEnabled,

[Description("device_verification.disabled")]
DeviceVerificationDisabled
}
}
116 changes: 116 additions & 0 deletions src/Crowdin.Api/SecurityLogs/SecurityLogsApiExecutor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

using System.Collections.Generic;
using System.Threading.Tasks;

using Crowdin.Api.Core;
using JetBrains.Annotations;

#nullable enable

namespace Crowdin.Api.SecurityLogs
{
public class SecurityLogsApiExecutor
{
private readonly ICrowdinApiClient _apiClient;
private readonly IJsonParser _jsonParser;

public SecurityLogsApiExecutor(ICrowdinApiClient apiClient)
{
_apiClient = apiClient;
_jsonParser = apiClient.DefaultJsonParser;
}

public SecurityLogsApiExecutor(ICrowdinApiClient apiClient, IJsonParser jsonParser)
{
_apiClient = apiClient;
_jsonParser = jsonParser;
}

/// <summary>
/// List User Security Logs. Documentation:
/// <a href="https://developer.crowdin.com/api/v2/#operation/api.users.security-logs.getMany">Crowdin API</a>
/// <a href="https://developer.crowdin.com/enterprise/api/v2/#operation/api.users.security-logs.getMany">Crowdin Enterprise API</a>
/// </summary>
[PublicAPI]
public async Task<ResponseList<SecurityLog>> ListUserSecurityLogs(
long userId,
int limit = 25,
int offset = 0,
SecurityLogEventType? eventType = null,
string? ipAddress = null)
{
string url = FormUrl_Logs(userId);

IDictionary<string, string> queryParams = Utils.CreateQueryParamsFromPaging(limit, offset);
queryParams.AddDescriptionEnumValueIfPresent("event", eventType);
queryParams.AddParamIfPresent("ipAddress", ipAddress);

CrowdinApiResult result = await _apiClient.SendGetRequest(url, queryParams);
return _jsonParser.ParseResponseList<SecurityLog>(result.JsonObject);
}

/// <summary>
/// Get User Security Log. Documentation:
/// <a href="https://developer.crowdin.com/api/v2/#operation/api.users.security-logs.get">Crowdin API</a>
/// <a href="https://developer.crowdin.com/enterprise/api/v2/#operation/api.users.security-logs.get">Crowdin Enterprise API</a>
/// </summary>
[PublicAPI]
public async Task<SecurityLog> GetUserSecurityLog(long userId, long securityLogId)
{
string url = FormUrl_LogId(userId, securityLogId);
CrowdinApiResult result = await _apiClient.SendGetRequest(url);
return _jsonParser.ParseResponseObject<SecurityLog>(result.JsonObject);
}

#region Helper methods

private static string FormUrl_Logs(long userId)
{
return $"/users/{userId}/security-logs";
}

private static string FormUrl_LogId(long userId, long securityLogId)
{
return $"/users/{userId}/security-logs/{securityLogId}";
}

#endregion

#region Enterprise API

/// <summary>
/// List Organization Security Logs. Documentation:
/// <a href="https://developer.crowdin.com/enterprise/api/v2/#operation/api.security-logs.getMany">Crowdin Enterprise API</a>
/// </summary>
[PublicAPI]
public async Task<ResponseList<SecurityLog>> ListOrganizationSecurityLogs(
int limit = 25,
int offset = 0,
SecurityLogEventType? eventType = null,
string? ipAddress = null,
long? userId = null)
{
IDictionary<string, string> queryParams = Utils.CreateQueryParamsFromPaging(limit, offset);
queryParams.AddDescriptionEnumValueIfPresent("event", eventType);
queryParams.AddParamIfPresent("ipAddress", ipAddress);
queryParams.AddParamIfPresent("userId", userId);

CrowdinApiResult result = await _apiClient.SendGetRequest("/security-logs", queryParams);
return _jsonParser.ParseResponseList<SecurityLog>(result.JsonObject);
}

/// <summary>
/// Get Organization Security Log. Documentation:
/// <a href="https://developer.crowdin.com/enterprise/api/v2/#operation/api.security-logs.get">Crowdin Enterprise API</a>
/// </summary>
[PublicAPI]
public async Task<SecurityLog> GetOrganizationSecurityLog(long securityLogId)
{
var url = $"/security-logs/{securityLogId}";
CrowdinApiResult result = await _apiClient.SendGetRequest(url);
return _jsonParser.ParseResponseObject<SecurityLog>(result.JsonObject);
}

#endregion
}
}
60 changes: 60 additions & 0 deletions tests/Crowdin.Api.Tests/Core/Resources/SecurityLogs.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions tests/Crowdin.Api.Tests/Core/Resources/SecurityLogs.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>

<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">

</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ListUserSecurityLogs_Response" xml:space="preserve">
<value>{
"data": [
{
"data": {
"id": 2,
"event": "application.connected",
"info": "Some info",
"userId": 4,
"location": "USA",
"ipAddress": "127.0.0.1",
"deviceName": "MacOs on MacBook",
"createdAt": "2019-09-19T15:10:43+00:00"
}
}
],
"pagination": {
"offset": 0,
"limit": 25
}
}</value>
</data>
<data name="GetUserSecurityLog_Response" xml:space="preserve">
<value>{
"data": {
"id": 2,
"event": "application.connected",
"info": "Some info",
"userId": 4,
"location": "USA",
"ipAddress": "127.0.0.1",
"deviceName": "MacOs on MacBook",
"createdAt": "2019-09-19T15:10:43+00:00"
}
}</value>
</data>
</root>
9 changes: 9 additions & 0 deletions tests/Crowdin.Api.Tests/Crowdin.Api.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Applications.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Resources\SecurityLogs.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>SecurityLogs.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
Expand Down Expand Up @@ -352,6 +356,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>Applications.resx</DependentUpon>
</Compile>
<Compile Update="Core\Resources\SecurityLogs.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>SecurityLogs.resx</DependentUpon>
</Compile>
</ItemGroup>

</Project>
Loading

0 comments on commit c378589

Please sign in to comment.