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

[ADMINAPI-1060] - Admin Console - Instances endpoint (/adminconsole/Instances) #178

Merged
merged 14 commits into from
Nov 6, 2024
13 changes: 12 additions & 1 deletion Application/Ed-Fi-ODS-AdminApi.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "Integra
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.Ods.AdminApi.DBTests", "EdFi.Ods.AdminApi.DBTests\EdFi.Ods.AdminApi.DBTests.csproj", "{73259EC2-4AA0-40C2-9C60-8AB1BF369CF5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdFi.Ods.AdminApi.AdminConsole", "EdFi.Ods.AdminApi.AdminConsole\EdFi.Ods.AdminApi.AdminConsole.csproj", "{0F34C4F6-F7A2-442A-9E54-FCBD9A00F914}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.Ods.AdminApi.AdminConsole", "EdFi.Ods.AdminApi.AdminConsole\EdFi.Ods.AdminApi.AdminConsole.csproj", "{0F34C4F6-F7A2-442A-9E54-FCBD9A00F914}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.Ods.AdminConsole.DBTests", "EdFi.Ods.AdminConsole.DBTests\EdFi.Ods.AdminConsole.DBTests.csproj", "{A2DC17AC-66C2-4119-BB47-4266E8ACB055}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -61,13 +63,22 @@ Global
{0F34C4F6-F7A2-442A-9E54-FCBD9A00F914}.Release|Any CPU.Build.0 = Release|Any CPU
{0F34C4F6-F7A2-442A-9E54-FCBD9A00F914}.Release|x64.ActiveCfg = Release|Any CPU
{0F34C4F6-F7A2-442A-9E54-FCBD9A00F914}.Release|x64.Build.0 = Release|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Debug|x64.ActiveCfg = Debug|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Debug|x64.Build.0 = Debug|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Release|Any CPU.Build.0 = Release|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Release|x64.ActiveCfg = Release|Any CPU
{A2DC17AC-66C2-4119-BB47-4266E8ACB055}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F62C9CF6-A632-4894-B61A-674198DAB86E} = {9A9D18B4-718D-4681-BAFE-A1C42E18A7CC}
{73259EC2-4AA0-40C2-9C60-8AB1BF369CF5} = {D8A26B59-6DAD-4046-9DDE-00D2CFDAE9B6}
{A2DC17AC-66C2-4119-BB47-4266E8ACB055} = {D8A26B59-6DAD-4046-9DDE-00D2CFDAE9B6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {30CF2BE4-58CA-4598-9B59-D334FC971A0F}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
Expand All @@ -24,6 +24,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Rijndael256" Version="3.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.0" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using System.ComponentModel.DataAnnotations;
using EdFi.Ods.AdminApi.AdminConsole.Infrastructure.Services.Instances.Commands;
using FluentValidation;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;

namespace EdFi.Ods.AdminApi.AdminConsole.Features.Instances;

public class AddInstance : IFeature
{
public void MapEndpoints(IEndpointRouteBuilder endpoints)
{
AdminApiAdminConsoleEndpointBuilder.MapPost(endpoints, "/instances", Execute)
.WithRouteOptions(b => b.WithResponseCode(201))
.BuildForVersions();
}

public async Task<IResult> Execute(Validator validator, IAddInstanceCommand addInstanceCommand, AddInstanceRequest request)
{
await validator.GuardAsync(request);
var addedInstanceResult = await addInstanceCommand.Execute(request);

return Results.Created($"/instances/{addedInstanceResult.DocId}", addedInstanceResult);
}

public class AddInstanceRequest : IAddInstanceModel
{
[Required]
public int InstanceId { get; set; }
public int? EdOrgId { get; set; }
[Required]
public int TenantId { get; set; }
[Required]
public string Document { get; set; }
}

public class Validator : AbstractValidator<AddInstanceRequest>
{
public Validator()
{
RuleFor(m => m.InstanceId)
.NotNull();

RuleFor(m => m.EdOrgId)
.NotNull();

RuleFor(m => m.Document)
.NotNull()
.NotEmpty()
.Must(BeValidDocument).WithMessage("Document must be a valid JSON.");
}

private bool BeValidDocument(string document)
{
try
{
Newtonsoft.Json.Linq.JToken.Parse(document);
return true;
}
catch (Newtonsoft.Json.JsonReaderException)
{
return false;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using Newtonsoft.Json;

namespace EdFi.Ods.AdminApi.AdminConsole.Features.OdsInstances;

public class InstanceModel
{
[JsonProperty("DocId")]
public int DocId { get; set; }

[JsonProperty("InstanceId")]
public int? InstanceId { get; set; }

[JsonProperty("TenantId")]
public int? TenantId { get; set; }

[JsonProperty("EdOrgId")]
public int? EdOrgId { get; set; }

[JsonProperty("Document")]
public string? Document { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using System.Linq;
using EdFi.Ods.AdminApi.AdminConsole.Features.OdsInstances;
using EdFi.Ods.AdminApi.AdminConsole.Infrastructure.DataAccess.Models;
using EdFi.Ods.AdminApi.AdminConsole.Infrastructure.Services.Instances.Queries;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;

namespace EdFi.Ods.AdminApi.AdminConsole.Features.UserProfiles;

public class ReadInstances : IFeature
{
public void MapEndpoints(IEndpointRouteBuilder endpoints)
{
AdminApiAdminConsoleEndpointBuilder.MapGet(endpoints, "/instances", GetInstances)
.BuildForVersions();

AdminApiAdminConsoleEndpointBuilder.MapGet(endpoints, "/instances/{id}", GetInstance)
.WithRouteOptions(b => b.WithResponse<InstanceModel>(200))
.BuildForVersions();
}

internal async Task<IResult> GetInstances([FromServices] IGetInstancesQuery getInstancesQuery)
{
var instances = await getInstancesQuery.Execute();
return Results.Ok(instances.Select(i => i.Document).ToList());
}

internal async Task<IResult> GetInstance([FromServices] IGetInstanceQuery getInstanceQuery, int tenantId)
{
var instance = await getInstanceQuery.Execute(tenantId);

if (instance != null)
return Results.Ok(instance.Document);

return Results.NotFound();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

namespace EdFi.Ods.AdminApi.AdminConsole.Helpers;

public class AdminConsoleSettings : IEncryptionKeySettings
{
public string EncryptionKey { get; set; } = string.Empty;
}
35 changes: 35 additions & 0 deletions Application/EdFi.Ods.AdminApi.AdminConsole/Helpers/Encryption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using Rijndael256;

namespace EdFi.Ods.AdminApi.AdminConsole.Helpers;
public class Encryption : RijndaelEtM
{
/// <summary>
/// Encrypts plaintext using the Encrypt-then-MAC (EtM) mode via the Rijndael cipher in
/// CBC mode with a password derived HMAC SHA-512 salt. A random 128-bit Initialization
/// Vector is generated for the cipher.
/// </summary>
/// <param name="plainText">The plainText to encrypt.</param>
/// <param name="encryptionKey">The encryptionKey to encrypt the plainText with.</param>
/// <returns>The Base64 encoded EtM ciphertext.</returns>
public static string Encrypt(string plainText, string encryptionKey)
{
return Encrypt(plainText, encryptionKey, KeySize.Aes256);
}

/// <summary>
/// Decrypts EtM ciphertext using the Rijndael cipher in CBC mode with a password derived
/// HMAC SHA-512 salt.
/// </summary>
/// <param name="ciphertText">The Base64 encoded EtM ciphertext to decrypt.</param>
/// <param name="encryptionKey">The encryptionKey to decrypt the EtM ciphertext with.</param>
/// <returns>The plaintext.</returns>
public static string Decrypt(string ciphertText, string encryptionKey)
{
return Decrypt(ciphertText, encryptionKey, KeySize.Aes256);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

namespace EdFi.Ods.AdminApi.AdminConsole.Helpers;

public interface IEncryptionKeyResolver
{
string GetEncryptionKey();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

namespace EdFi.Ods.AdminApi.AdminConsole.Helpers;

public interface IEncryptionKeySettings
{
public string EncryptionKey { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

namespace EdFi.Ods.AdminApi.AdminConsole.Helpers;

public class OptionsEncryptionKeyResolver : IEncryptionKeyResolver
{
private readonly string _encryptionKey;

public OptionsEncryptionKeyResolver(IEncryptionKeySettings encryptionKeySettings)
{
_encryptionKey = encryptionKeySettings.EncryptionKey;
}

public string GetEncryptionKey()
{
return _encryptionKey;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using Microsoft.EntityFrameworkCore.Migrations;
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

Expand Down

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

Loading