-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* start and create api keys -screen * Added revoked column * Added migration for new column * Added repository method to get list of Api keys * Added model * Added use case to get Api keys based on Organisation id * Added use case to create new api key * Added test to register authentication key * Added Register Authentication key endpoint in api * updated return type of endpoints * Added Patch endpoint to revoke authentication key Added repo method Added test for repo & usecase * Added revoke unctionality Api key listing unit test * APIKey Name already exist check while saving * Added authorize policy for editor role * PR changes for api endpoint * Added new column and made revoved non-nullable * Fix migration * Added auth key revoke consideration * used RevokedOn instead of UpdatedOn --------- Co-authored-by: Dharm <[email protected]>
- Loading branch information
1 parent
6aaf5b3
commit 8708d07
Showing
38 changed files
with
3,175 additions
and
15 deletions.
There are no files selected for viewing
123 changes: 123 additions & 0 deletions
123
Frontend/CO.CDP.OrganisationApp.Tests/Pages/ApiKeyManagement/CreateApiKeyTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
using Amazon.S3; | ||
using CO.CDP.Organisation.WebApiClient; | ||
using CO.CDP.OrganisationApp.Constants; | ||
using CO.CDP.OrganisationApp.Pages.ApiKeyManagement; | ||
using FluentAssertions; | ||
using FluentAssertions.Equivalency; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.ModelBinding; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
using Moq; | ||
using System.Net; | ||
using ProblemDetails = CO.CDP.Organisation.WebApiClient.ProblemDetails; | ||
|
||
namespace CO.CDP.OrganisationApp.Tests.Pages.ApiKeyManagement; | ||
|
||
public class CreateApiKeyTest | ||
{ | ||
private readonly Mock<IOrganisationClient> _mockOrganisationClient = new(); | ||
private readonly Guid _organisationId = Guid.NewGuid(); | ||
private readonly CreateApiKeyModel _model; | ||
public CreateApiKeyTest() | ||
{ | ||
_model = new CreateApiKeyModel(_mockOrganisationClient.Object); | ||
} | ||
|
||
[Fact] | ||
public async Task OnPost_WhenModelStateIsInvalid_ShouldReturnPage() | ||
{ | ||
_model.ModelState.AddModelError("key", "error"); | ||
|
||
var result = await _model.OnPost(); | ||
|
||
result.Should().BeOfType<PageResult>(); | ||
} | ||
|
||
[Fact] | ||
public async Task OnPost_WhenApiExceptionOccurs_ShouldRedirectToPageNotFound() | ||
{ | ||
_mockOrganisationClient.Setup(client => client.CreateAuthenticationKeyAsync(It.IsAny<Guid>(), It.IsAny<RegisterAuthenticationKey>())) | ||
.ThrowsAsync(new ApiException(string.Empty, (int)HttpStatusCode.NotFound, string.Empty, null, null)); | ||
|
||
_model.Id = Guid.NewGuid(); | ||
_model.ApiKeyName = "TestApiKey"; | ||
|
||
var result = await _model.OnPost(); | ||
|
||
result.Should().BeOfType<RedirectResult>() | ||
.Which.Url.Should().Be("/page-not-found"); | ||
} | ||
|
||
[Fact] | ||
public async Task OnPost_ShouldRedirectToManageApiKey() | ||
{ | ||
_model.Id = Guid.NewGuid(); | ||
|
||
_mockOrganisationClient | ||
.Setup(c => c.CreateAuthenticationKeyAsync(_organisationId, DummyApiKeyEntity())); | ||
|
||
var result = await _model.OnPost(); | ||
|
||
var redirectToPageResult = result.Should().BeOfType<RedirectToPageResult>().Subject; | ||
|
||
_mockOrganisationClient.Verify(c => c.CreateAuthenticationKeyAsync(It.IsAny<Guid>(), It.IsAny<RegisterAuthenticationKey>()), Times.Once()); | ||
|
||
redirectToPageResult.PageName.Should().Be("NewApiKeyDetails"); | ||
} | ||
|
||
[Fact] | ||
public async Task OnPost_ShouldReturnPage_WhenCreateApiKeyModelStateIsInvalid() | ||
{ | ||
var model = new CreateApiKeyModel(_mockOrganisationClient.Object); | ||
|
||
model.ModelState.AddModelError("ApiKeyName", "Enter the api key name"); | ||
|
||
var result = await model.OnPost(); | ||
|
||
result.Should().BeOfType<PageResult>(); | ||
} | ||
|
||
[Theory] | ||
[InlineData(ErrorCodes.APIKEY_NAME_ALREADY_EXISTS, ErrorMessagesList.DuplicateApiKeyName, StatusCodes.Status400BadRequest)] | ||
public async Task OnPost_AddsModelError(string errorCode, string expectedErrorMessage, int statusCode) | ||
{ | ||
var problemDetails = new ProblemDetails( | ||
title: errorCode, | ||
detail: ErrorMessagesList.DuplicateApiKeyName, | ||
status: statusCode, | ||
instance: null, | ||
type: null | ||
) | ||
{ | ||
AdditionalProperties = | ||
{ | ||
{ "code", "APIKEY_NAME_ALREADY_EXISTS" } | ||
} | ||
}; | ||
var aex = new ApiException<ProblemDetails>( | ||
"Duplicate Api key name", | ||
statusCode, | ||
"Bad Request", | ||
null, | ||
problemDetails ?? new ProblemDetails("Detail", "Instance", statusCode, "Problem title", "Problem type"), | ||
null | ||
); | ||
|
||
_mockOrganisationClient.Setup(client => client.CreateAuthenticationKeyAsync(It.IsAny<Guid>(), It.IsAny<RegisterAuthenticationKey>())) | ||
.ThrowsAsync(aex); | ||
|
||
_model.Id = Guid.NewGuid(); | ||
_model.ApiKeyName = "TestApiKey"; | ||
|
||
var result = await _model.OnPost(); | ||
|
||
_model.ModelState[string.Empty].As<ModelStateEntry>().Errors | ||
.Should().Contain(e => e.ErrorMessage == expectedErrorMessage); | ||
} | ||
|
||
private RegisterAuthenticationKey DummyApiKeyEntity() | ||
{ | ||
return new RegisterAuthenticationKey(key: "_key", name: "name", organisationId: _organisationId); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
Frontend/CO.CDP.OrganisationApp.Tests/Pages/ApiKeyManagement/ManageApiKeyTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
using CO.CDP.Organisation.WebApiClient; | ||
using CO.CDP.OrganisationApp.Pages.ApiKeyManagement; | ||
using FluentAssertions; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
using Moq; | ||
using System.Net; | ||
|
||
namespace CO.CDP.OrganisationApp.Tests.Pages.ApiKeyManagement; | ||
|
||
public class ManageApiKeyTest | ||
{ | ||
private readonly Mock<IOrganisationClient> _mockOrganisationClient = new(); | ||
private readonly ManageApiKeyModel _model; | ||
|
||
public ManageApiKeyTest() | ||
{ | ||
_model = new ManageApiKeyModel(_mockOrganisationClient.Object); | ||
_model.Id = Guid.NewGuid(); | ||
} | ||
|
||
[Fact] | ||
public async Task OnGet_WhenAuthenticationKeysAreRetrieved_ShouldReturnPage() | ||
{ | ||
var authenticationKeys = new List<AuthenticationKey> { | ||
new AuthenticationKey(createdOn: DateTimeOffset.UtcNow.AddDays(-1), name: "TestKey1", revoked: false, revokedOn: DateTimeOffset.UtcNow) | ||
}; | ||
|
||
_mockOrganisationClient | ||
.Setup(client => client.GetAuthenticationKeysAsync(It.IsAny<Guid>())) | ||
.ReturnsAsync(authenticationKeys); | ||
|
||
var result = await _model.OnGet(); | ||
|
||
result.Should().BeOfType<PageResult>(); | ||
|
||
_model.AuthenticationKeys.Should().HaveCount(1); | ||
_model.AuthenticationKeys.FirstOrDefault()!.Name.Should().Be("TestKey1"); | ||
} | ||
|
||
[Fact] | ||
public async Task OnGet_WhenApiExceptionOccurs_ShouldRedirectToPageNotFound() | ||
{ | ||
_mockOrganisationClient.Setup(client => client.GetAuthenticationKeysAsync(It.IsAny<Guid>())) | ||
.ThrowsAsync(new ApiException(string.Empty, (int)HttpStatusCode.NotFound, string.Empty, null, null)); | ||
|
||
var result = await _model.OnGet(); | ||
|
||
result.Should().BeOfType<RedirectResult>() | ||
.Which.Url.Should().Be("/page-not-found"); | ||
} | ||
|
||
[Fact] | ||
public void OnPost_ShouldRedirectToCreateApiKeyPage() | ||
{ | ||
var result = _model.OnPost(); | ||
|
||
result.Should().BeOfType<RedirectToPageResult>() | ||
.Which.PageName.Should().Be("CreateApiKey"); | ||
|
||
result.As<RedirectToPageResult>().RouteValues!["Id"].Should().Be(_model.Id); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
Frontend/CO.CDP.OrganisationApp.Tests/Pages/ApiKeyManagement/NewApiKeyDetailsTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using CO.CDP.OrganisationApp.Pages.ApiKeyManagement; | ||
using FluentAssertions; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
|
||
namespace CO.CDP.OrganisationApp.Tests.Pages.ApiKeyManagement; | ||
|
||
public class NewApiKeyDetailsTest | ||
{ | ||
[Fact] | ||
public void OnGet_ShouldReturnPageResult() | ||
{ | ||
var pageModel = new NewApiKeyDetailsModel | ||
{ | ||
Id = Guid.NewGuid(), | ||
ApiKey = "test-api-key" | ||
}; | ||
|
||
var result = pageModel.OnGet(); | ||
|
||
result.Should().BeOfType<PageResult>(); | ||
pageModel.Id.Should().NotBeEmpty(); | ||
pageModel.ApiKey.Should().Be("test-api-key"); | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
Frontend/CO.CDP.OrganisationApp.Tests/Pages/ApiKeyManagement/RevokeApiKeyTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using CO.CDP.Organisation.WebApiClient; | ||
using CO.CDP.OrganisationApp.Pages.ApiKeyManagement; | ||
using FluentAssertions; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
using Moq; | ||
using System.Net; | ||
|
||
namespace CO.CDP.OrganisationApp.Tests.Pages.ApiKeyManagement; | ||
|
||
public class RevokeApiKeyTest | ||
{ | ||
private readonly Mock<IOrganisationClient> _mockOrganisationClient; | ||
private readonly RevokeApiKeyModel _pageModel; | ||
|
||
public RevokeApiKeyTest() | ||
{ | ||
_mockOrganisationClient = new Mock<IOrganisationClient>(); | ||
_pageModel = new RevokeApiKeyModel(_mockOrganisationClient.Object) | ||
{ | ||
Id = Guid.NewGuid(), | ||
ApiKeyName = "TestApiKey" | ||
}; | ||
} | ||
|
||
[Fact] | ||
public async Task OnPost_ValidModelState_ShouldRedirectToManageApiKeyPage() | ||
{ | ||
_mockOrganisationClient | ||
.Setup(client => client.RevokeAuthenticationKeyAsync(It.IsAny<Guid>(), It.IsAny<string>())) | ||
.Returns(Task.CompletedTask); | ||
|
||
var result = await _pageModel.OnPost(); | ||
|
||
result.Should().BeOfType<RedirectToPageResult>() | ||
.Which.PageName.Should().Be("ManageApiKey"); | ||
result.As<RedirectToPageResult>().RouteValues!["Id"].Should().Be(_pageModel.Id); | ||
|
||
_mockOrganisationClient.Verify(client => client.RevokeAuthenticationKeyAsync(_pageModel.Id, _pageModel.ApiKeyName), Times.Once); | ||
} | ||
|
||
[Fact] | ||
public async Task OnPost_InvalidModelState_ShouldReturnPageResult() | ||
{ | ||
_pageModel.ModelState.AddModelError("ApiKeyName", "ApiKeyName is required"); | ||
|
||
var result = await _pageModel.OnPost(); | ||
|
||
result.Should().BeOfType<PageResult>(); | ||
|
||
_mockOrganisationClient.Verify(client => client.RevokeAuthenticationKeyAsync(It.IsAny<Guid>(), It.IsAny<string>()), Times.Never); | ||
} | ||
|
||
[Fact] | ||
public async Task OnPost_ApiException404_ShouldRedirectToPageNotFound() | ||
{ | ||
_mockOrganisationClient.Setup(client => client.RevokeAuthenticationKeyAsync(It.IsAny<Guid>(), It.IsAny<string>())) | ||
.ThrowsAsync(new ApiException(string.Empty, (int)HttpStatusCode.NotFound, string.Empty, null, null)); | ||
|
||
var result = await _pageModel.OnPost(); | ||
|
||
result.Should().BeOfType<RedirectResult>() | ||
.Which.Url.Should().Be("/page-not-found"); | ||
|
||
_mockOrganisationClient.Verify(client => client.RevokeAuthenticationKeyAsync(_pageModel.Id, _pageModel.ApiKeyName), Times.Once); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
Frontend/CO.CDP.OrganisationApp/Pages/ApiKeyManagement/CreateApiKey.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
@page "/organisation/{id}/manage-api-key/create" | ||
@model CreateApiKeyModel | ||
|
||
@{ | ||
var apiKeyNameHasError = ((TagBuilder)Html.ValidationMessageFor(m => m.ApiKeyName)).HasInnerHtml; | ||
} | ||
<a href="/organisation/@Model.Id/supplier-information/manage-api-key/start" class="govuk-back-link">Back</a> | ||
|
||
<main class="govuk-main-wrapper"> | ||
<div class="govuk-grid-row"> | ||
<div class="govuk-grid-column-two-thirds"> | ||
<partial name="_ErrorSummary" model="@ModelState" /> | ||
<form class="form" method="post"> | ||
<div class="govuk-form-group"> | ||
<h1 class="govuk-label-wrapper"> | ||
<label class="govuk-label govuk-label--l" for="APIKeyName"> | ||
Name your API key | ||
</label> | ||
</h1> | ||
<div id="hint" class="govuk-hint"> | ||
For security we only show the API key once on the confirmation page. Add a name for reference. For example, the name of the eSender it's for. | ||
</div> | ||
<div class="govuk-form-group @(apiKeyNameHasError ? "govuk-form-group--error" : "")"> | ||
@if (apiKeyNameHasError) | ||
{ | ||
<p id="api-key-name-error" class="govuk-error-message"> | ||
<span class="govuk-visually-hidden">Error:</span> | ||
@Html.ValidationMessageFor(m => m.ApiKeyName) | ||
</p> | ||
} | ||
<input class="govuk-input govuk-!-width-two-thirds @(apiKeyNameHasError ? "govuk-input--error" : "")" | ||
id="@nameof(Model.ApiKeyName)" value="@Model.ApiKeyName" name="@nameof(Model.ApiKeyName)" type="text" | ||
@(apiKeyNameHasError ? "aria-describedby=api-key-name-error" : "")> | ||
|
||
</div> | ||
</div> | ||
|
||
<div class="govuk-button-group govuk-!-margin-top-6"> | ||
<button class="govuk-button" data-module="govuk-button" type="submit"> | ||
Continue | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
</main> |
Oops, something went wrong.