mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
Merged PR 32477: Added Snyk API console app
Added Snyk API console app
This commit is contained in:
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
|
// Use hover for the description of the existing attributes
|
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||||
|
"name": ".NET Core Launch (console)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
|
"program": "${workspaceFolder}/ConsoleApps/AzureRestApi/AzureRestApi/bin/Debug/net6.0/AzureRestApi.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ConsoleApps/AzureRestApi/AzureRestApi",
|
||||||
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
"console": "internalConsole",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
41
.vscode/tasks.json
vendored
Normal file
41
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ConsoleApps/AzureRestApi/AzureRestApi/AzureRestApi.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "publish",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"publish",
|
||||||
|
"${workspaceFolder}/ConsoleApps/AzureRestApi/AzureRestApi/AzureRestApi.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "watch",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"watch",
|
||||||
|
"run",
|
||||||
|
"--project",
|
||||||
|
"${workspaceFolder}/ConsoleApps/AzureRestApi/AzureRestApi/AzureRestApi.csproj"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
25
ConsoleApps/SnykRestApi/SnykRestApi.sln
Normal file
25
ConsoleApps/SnykRestApi/SnykRestApi.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.2.32505.173
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnykRestApi", "SnykRestApi\SnykRestApi.csproj", "{388306F9-E67B-4CD2-9876-ACAC06968015}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{388306F9-E67B-4CD2-9876-ACAC06968015}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{388306F9-E67B-4CD2-9876-ACAC06968015}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{388306F9-E67B-4CD2-9876-ACAC06968015}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{388306F9-E67B-4CD2-9876-ACAC06968015}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {BA8B2157-BFB1-4397-9DED-E4522D895591}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace SnykRestApi.Models.Parsed
|
||||||
|
{
|
||||||
|
public class AuditLog
|
||||||
|
{
|
||||||
|
public string GroupId { get; set; }
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
public string OrganizationName { get; set; }
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string ProjectId { get; set; }
|
||||||
|
public string ProjectName { get; set; }
|
||||||
|
public string Event { get; set; }
|
||||||
|
public DateTime Created { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Models.Raw
|
||||||
|
{
|
||||||
|
internal class AuditLogResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("groupId")]
|
||||||
|
public string GroupId { get; set; }
|
||||||
|
[JsonPropertyName("orgId")]
|
||||||
|
public string OrgId { get; set; }
|
||||||
|
[JsonPropertyName("userId")]
|
||||||
|
public string UserId { get; set; }
|
||||||
|
[JsonPropertyName("projectId")]
|
||||||
|
public string ProjectId { get; set; }
|
||||||
|
[JsonPropertyName("event")]
|
||||||
|
public string Event { get; set; }
|
||||||
|
//[JsonPropertyName("content")]
|
||||||
|
//public string Content { get; set; }
|
||||||
|
[JsonPropertyName("created")]
|
||||||
|
public DateTime Created { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Models.Raw
|
||||||
|
{
|
||||||
|
internal class GroupResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Models.Raw
|
||||||
|
{
|
||||||
|
internal class OrganizationListResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("orgs")]
|
||||||
|
public List<OrganizationResponse> Orgs { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Models.Raw
|
||||||
|
{
|
||||||
|
internal class OrganizationResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
[JsonPropertyName("slug")]
|
||||||
|
public string Slug { get; set; }
|
||||||
|
[JsonPropertyName("url")]
|
||||||
|
public string Url { get; set; }
|
||||||
|
[JsonPropertyName("group")]
|
||||||
|
public GroupResponse Group { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Models.Raw
|
||||||
|
{
|
||||||
|
internal class ProjectListResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("org")]
|
||||||
|
public OrganizationResponse Org { get; set; }
|
||||||
|
[JsonPropertyName("projects")]
|
||||||
|
public List<ProjectResponse> Projects { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Models.Raw
|
||||||
|
{
|
||||||
|
internal class ProjectResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Models.Raw
|
||||||
|
{
|
||||||
|
internal class UserResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
[JsonPropertyName("username")]
|
||||||
|
public string UserName { get; set; }
|
||||||
|
[JsonPropertyName("email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
11
ConsoleApps/SnykRestApi/SnykRestApi/Models/Settings.cs
Normal file
11
ConsoleApps/SnykRestApi/SnykRestApi/Models/Settings.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace SnykRestApi.Models
|
||||||
|
{
|
||||||
|
public class Settings
|
||||||
|
{
|
||||||
|
public string KeyVaultName { get; set; } = string.Empty;
|
||||||
|
public string CsvFolder { get; set; } = string.Empty;
|
||||||
|
public string SnykBaseUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
40
ConsoleApps/SnykRestApi/SnykRestApi/Program.cs
Normal file
40
ConsoleApps/SnykRestApi/SnykRestApi/Program.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using SnykRestApi.Models;
|
||||||
|
using SnykRestApi.Repositories;
|
||||||
|
using SnykRestApi.Services;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace SnykRestApi
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static Task Main(string[] args) =>
|
||||||
|
CreateHostBuilder(args).Build().RunAsync();
|
||||||
|
|
||||||
|
static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureServices((builder, services) =>
|
||||||
|
{
|
||||||
|
IConfiguration config = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Settings settings = config.GetRequiredSection("Settings").Get<Settings>();
|
||||||
|
|
||||||
|
services.AddSingleton<AccessTokenRepository>();
|
||||||
|
services.AddSingleton(settings);
|
||||||
|
|
||||||
|
services.AddHttpClient<OrganizationRepository>();
|
||||||
|
services.AddHttpClient<AuditLogRepository>();
|
||||||
|
services.AddHttpClient<ProjectRepository>();
|
||||||
|
services.AddHttpClient<UserRepository>();
|
||||||
|
services.AddTransient<CsvRepository>();
|
||||||
|
|
||||||
|
services.AddScoped<AuditLogService>();
|
||||||
|
|
||||||
|
services.AddHostedService<OptionService>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Azure.Identity;
|
||||||
|
using Azure.Security.KeyVault.Secrets;
|
||||||
|
using SnykRestApi.Models;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Repositories
|
||||||
|
{
|
||||||
|
public class AccessTokenRepository
|
||||||
|
{
|
||||||
|
private readonly Settings _settings;
|
||||||
|
private string? _authorizationToken = string.Empty;
|
||||||
|
|
||||||
|
public AccessTokenRepository(Settings settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetAuthorizationToken()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(_authorizationToken)) return _authorizationToken;
|
||||||
|
|
||||||
|
var keyvaultUri = "https://" + _settings.KeyVaultName + ".vault.azure.net";
|
||||||
|
var credential = new DefaultAzureCredential();
|
||||||
|
var client = new SecretClient(new Uri(keyvaultUri), credential);
|
||||||
|
_authorizationToken = (await client.GetSecretAsync("SnykKey")).Value.Value;
|
||||||
|
|
||||||
|
return _authorizationToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using SnykRestApi.Models;
|
||||||
|
using SnykRestApi.Models.Raw;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Repositories
|
||||||
|
{
|
||||||
|
public class AuditLogRepository
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly AccessTokenRepository _accessTokenRepository;
|
||||||
|
private readonly Settings _settings;
|
||||||
|
|
||||||
|
public AuditLogRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_accessTokenRepository = accessTokenRepository;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<List<AuditLogResponse>> GetByOrganizationId(string origanizationId)
|
||||||
|
{
|
||||||
|
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
||||||
|
List<AuditLogResponse> result = new();
|
||||||
|
List<AuditLogResponse> responseItems;
|
||||||
|
|
||||||
|
int page = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
HttpRequestMessage request = new(HttpMethod.Post, $"{_settings.SnykBaseUrl}org/{origanizationId}/audit?from=2022-07-01&page={++page}");
|
||||||
|
request.Headers.Accept.Clear();
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
|
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
responseItems = JsonSerializer.Deserialize<List<AuditLogResponse>>(responseString) ?? new List<AuditLogResponse>();
|
||||||
|
result.AddRange(responseItems);
|
||||||
|
}
|
||||||
|
while (responseItems.Count == 100);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using CsvHelper;
|
||||||
|
using SnykRestApi.Models;
|
||||||
|
using SnykRestApi.Models.Parsed;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Repositories
|
||||||
|
{
|
||||||
|
public class CsvRepository
|
||||||
|
{
|
||||||
|
private readonly Settings _settings;
|
||||||
|
|
||||||
|
public CsvRepository(Settings settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task WriteAll (List<AuditLog> log)
|
||||||
|
{
|
||||||
|
var t = DateTime.Now;
|
||||||
|
var fileName = $"{_settings.CsvFolder}SnykAuditLog_{t:yyyy}{t:MM}{t:dd}_{t:HH}{t:mm}{t:ss}_{t:FFF}.csv";
|
||||||
|
|
||||||
|
using (var writer = new StreamWriter(fileName))
|
||||||
|
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
|
||||||
|
{
|
||||||
|
await csv.WriteRecordsAsync(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using SnykRestApi.Models;
|
||||||
|
using SnykRestApi.Models.Raw;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Repositories
|
||||||
|
{
|
||||||
|
public class OrganizationRepository
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly AccessTokenRepository _accessTokenRepository;
|
||||||
|
private readonly Settings _settings;
|
||||||
|
|
||||||
|
public OrganizationRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_accessTokenRepository = accessTokenRepository;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<List<OrganizationResponse>> GetAll()
|
||||||
|
{
|
||||||
|
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
||||||
|
|
||||||
|
var uri = $"{_settings.SnykBaseUrl}orgs";
|
||||||
|
HttpRequestMessage request = new(HttpMethod.Get, uri);
|
||||||
|
request.Headers.Accept.Clear();
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
|
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
var result = JsonSerializer.Deserialize<OrganizationListResponse>(responseString)?.Orgs;
|
||||||
|
return result ?? new List<OrganizationResponse>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using SnykRestApi.Models;
|
||||||
|
using SnykRestApi.Models.Raw;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Repositories
|
||||||
|
{
|
||||||
|
public class ProjectRepository
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly AccessTokenRepository _accessTokenRepository;
|
||||||
|
private readonly Settings _settings;
|
||||||
|
|
||||||
|
public ProjectRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_accessTokenRepository = accessTokenRepository;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<List<ProjectResponse>> GetAll(string organizationId)
|
||||||
|
{
|
||||||
|
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
||||||
|
|
||||||
|
var uri = $"{_settings.SnykBaseUrl}org/{organizationId}/projects";
|
||||||
|
HttpRequestMessage request = new(HttpMethod.Post, uri);
|
||||||
|
request.Headers.Accept.Clear();
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
|
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
var result = JsonSerializer.Deserialize<ProjectListResponse>(responseString)?.Projects;
|
||||||
|
return result ?? new List<ProjectResponse>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using SnykRestApi.Models;
|
||||||
|
using SnykRestApi.Models.Raw;
|
||||||
|
using Spectre.Console;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Repositories
|
||||||
|
{
|
||||||
|
public class UserRepository
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly AccessTokenRepository _accessTokenRepository;
|
||||||
|
private readonly Settings _settings;
|
||||||
|
|
||||||
|
public UserRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_accessTokenRepository = accessTokenRepository;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<List<UserResponse>> GetAll(List<string> ids)
|
||||||
|
{
|
||||||
|
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
||||||
|
List<UserResponse> result = new();
|
||||||
|
UserResponse? responseItem;
|
||||||
|
|
||||||
|
foreach (var id in ids)
|
||||||
|
{
|
||||||
|
HttpRequestMessage request = new(HttpMethod.Get, $"{_settings.SnykBaseUrl}user/{id}");
|
||||||
|
request.Headers.Accept.Clear();
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
|
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
responseItem = JsonSerializer.Deserialize<UserResponse>(responseString);
|
||||||
|
|
||||||
|
if (responseItem != null)
|
||||||
|
{
|
||||||
|
result.Add(responseItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine($"[red bold]Could not find user with id '{id}'[/]");
|
||||||
|
result.Add(new()
|
||||||
|
{
|
||||||
|
Id = id,
|
||||||
|
Name = "{ Unknown user }",
|
||||||
|
UserName = "{ Unknown user }",
|
||||||
|
Email = "{ Unknown user }"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
109
ConsoleApps/SnykRestApi/SnykRestApi/Services/AuditLogService.cs
Normal file
109
ConsoleApps/SnykRestApi/SnykRestApi/Services/AuditLogService.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
using SnykRestApi.Models.Parsed;
|
||||||
|
using SnykRestApi.Models.Raw;
|
||||||
|
using SnykRestApi.Repositories;
|
||||||
|
using Spectre.Console;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Services
|
||||||
|
{
|
||||||
|
public class AuditLogService
|
||||||
|
{
|
||||||
|
private readonly OrganizationRepository _organizationRepository;
|
||||||
|
private readonly AuditLogRepository _auditLogRepository;
|
||||||
|
private readonly ProjectRepository _projectRepository;
|
||||||
|
private readonly UserRepository _userRepository;
|
||||||
|
private readonly CsvRepository _csvRepository;
|
||||||
|
|
||||||
|
public AuditLogService(OrganizationRepository organizationRepository, AuditLogRepository auditLogRepostitory, ProjectRepository projectRepository, UserRepository userRepository, CsvRepository csvRepository)
|
||||||
|
{
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
_auditLogRepository = auditLogRepostitory;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_userRepository = userRepository;
|
||||||
|
_csvRepository = csvRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateAuditLog ()
|
||||||
|
{
|
||||||
|
var rule = new Rule("[skyblue1]Creating Snyk Audit Log CSV[/]");
|
||||||
|
rule.Alignment = Justify.Left;
|
||||||
|
rule.Style = Style.Parse("skyblue1");
|
||||||
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
|
await AnsiConsole.Status()
|
||||||
|
.AutoRefresh(true)
|
||||||
|
.Spinner(Spinner.Known.Default)
|
||||||
|
.StartAsync("Retrieving organizations...", async ctx =>
|
||||||
|
{
|
||||||
|
var organizations = await _organizationRepository.GetAll();
|
||||||
|
if (organizations == null || !organizations.Any()) throw new Exception("No organizations found");
|
||||||
|
|
||||||
|
var log = new List<AuditLogResponse>();
|
||||||
|
var projects = new List<ProjectResponse>();
|
||||||
|
|
||||||
|
foreach (var organization in organizations)
|
||||||
|
{
|
||||||
|
rule = new Rule($"[skyblue1]{organization.Name}[/]");
|
||||||
|
rule.Alignment = Justify.Left;
|
||||||
|
rule.Style = Style.Parse("skyblue1 dim");
|
||||||
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
|
ctx.Status($"Getting the projects for organization '{organization.Name}'");
|
||||||
|
var orgProjects = await _projectRepository.GetAll(organization.Id);
|
||||||
|
projects.AddRange(orgProjects);
|
||||||
|
AnsiConsole.WriteLine($"Got {orgProjects.Count} projects.");
|
||||||
|
|
||||||
|
ctx.Status($"Getting the audit log for organization '{organization.Name}'");
|
||||||
|
var orgLogs = await _auditLogRepository.GetByOrganizationId(organization.Id);
|
||||||
|
log.AddRange(orgLogs);
|
||||||
|
AnsiConsole.WriteLine($"Got {orgLogs.Count} log records.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rule = new Rule($"[skyblue1]Retrieving users[/]");
|
||||||
|
rule.Alignment = Justify.Left;
|
||||||
|
rule.Style = Style.Parse("skyblue1 dim");
|
||||||
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
|
ctx.Status($"Getting users");
|
||||||
|
var userIds = log.Select(l => l.UserId).Distinct().ToList();
|
||||||
|
|
||||||
|
var users = await _userRepository.GetAll(userIds);
|
||||||
|
AnsiConsole.WriteLine($"Got {users.Count} users of {userIds.Count} user ids.");
|
||||||
|
|
||||||
|
rule = new Rule($"[skyblue1]Creating CSV[/]");
|
||||||
|
rule.Alignment = Justify.Left;
|
||||||
|
rule.Style = Style.Parse("skyblue1 dim");
|
||||||
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
|
ctx.Status($"Combining all information");
|
||||||
|
var result = (from l in log
|
||||||
|
join o in organizations on l.OrgId equals o.Id into gjO
|
||||||
|
from subO in gjO.DefaultIfEmpty()
|
||||||
|
join u in users on l.UserId equals u.Id into gjU
|
||||||
|
from subU in gjU.DefaultIfEmpty()
|
||||||
|
join p in projects on l.ProjectId equals p.Id into gjP
|
||||||
|
from subP in gjP.DefaultIfEmpty()
|
||||||
|
select new AuditLog()
|
||||||
|
{
|
||||||
|
GroupId = l.GroupId,
|
||||||
|
OrganizationId = l.OrgId,
|
||||||
|
OrganizationName = subO?.Name,
|
||||||
|
ProjectId = l.ProjectId,
|
||||||
|
ProjectName = subP?.Name,
|
||||||
|
UserId = l.UserId,
|
||||||
|
UserName = subU?.Name,
|
||||||
|
Event = l.Event,
|
||||||
|
Created = l.Created
|
||||||
|
}).ToList();
|
||||||
|
AnsiConsole.WriteLine($"Prepared {result.Count} lines to export of {log.Count} audit log records.");
|
||||||
|
|
||||||
|
ctx.Status($"Writing CSV");
|
||||||
|
await _csvRepository.WriteAll(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace SnykRestApi.Services
|
||||||
|
{
|
||||||
|
public class OptionService : IHostedService
|
||||||
|
{
|
||||||
|
private readonly ILogger<OptionService> _logger;
|
||||||
|
private readonly AuditLogService _auditLogService;
|
||||||
|
|
||||||
|
public OptionService(ILogger<OptionService> logger, AuditLogService auditLogService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_auditLogService = auditLogService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var rule = new Rule("[yellow]Cloud Egineering Console App[/]");
|
||||||
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
|
|
||||||
|
Console.WriteLine("-- This couldn't be done in the Snyk UI, so here we are.... ");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
var choices = new[]
|
||||||
|
{
|
||||||
|
"Create Audit log CSV.",
|
||||||
|
"Exit"
|
||||||
|
};
|
||||||
|
var result = AnsiConsole.Prompt(new SelectionPrompt<string>()
|
||||||
|
.Title("Select what you want to do:")
|
||||||
|
.PageSize(10)
|
||||||
|
.MoreChoicesText("[grey](Move up and down to reveal more choices)[/]")
|
||||||
|
.AddChoices(choices));
|
||||||
|
|
||||||
|
if (result == choices[0])
|
||||||
|
{
|
||||||
|
await _auditLogService.CreateAuditLog();
|
||||||
|
}
|
||||||
|
//else if (result == choices[1])
|
||||||
|
//{
|
||||||
|
// // Do something
|
||||||
|
//}
|
||||||
|
|
||||||
|
rule = new Rule("[yellow]Done. Bye.[/]");
|
||||||
|
AnsiConsole.Write(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
ConsoleApps/SnykRestApi/SnykRestApi/SnykRestApi.csproj
Normal file
28
ConsoleApps/SnykRestApi/SnykRestApi/SnykRestApi.csproj
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Identity" Version="1.6.1" />
|
||||||
|
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.3.0" />
|
||||||
|
<PackageReference Include="CsvHelper" Version="28.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Spectre.Console" Version="0.44.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
14
ConsoleApps/SnykRestApi/SnykRestApi/appsettings.json
Normal file
14
ConsoleApps/SnykRestApi/SnykRestApi/appsettings.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Settings": {
|
||||||
|
"KeyVaultName": "consoleapp",
|
||||||
|
"CsvFolder": "c:\\temp\\",
|
||||||
|
"SnykBaseUrl": "https://api.snyk.io/api/v1/"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Warning",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "None"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user