diff --git a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj
index 66ef0e48..71b01d13 100644
--- a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj
+++ b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj
@@ -5,9 +5,9 @@
-
-
-
+
+
+
diff --git a/TransactionProcessor.Client/ITransactionProcessorClient.cs b/TransactionProcessor.Client/ITransactionProcessorClient.cs
index 38c6bd9a..bd841d71 100644
--- a/TransactionProcessor.Client/ITransactionProcessorClient.cs
+++ b/TransactionProcessor.Client/ITransactionProcessorClient.cs
@@ -1,5 +1,6 @@
namespace TransactionProcessor.Client
{
+ using System;
using System.Threading;
using System.Threading.Tasks;
using DataTransferObjects;
@@ -14,7 +15,8 @@ public interface ITransactionProcessorClient
/// The transaction request.
/// The cancellation token.
///
- Task PerformTransaction(SerialisedMessage transactionRequest,
+ Task PerformTransaction(String accessToken,
+ SerialisedMessage transactionRequest,
CancellationToken cancellationToken);
#endregion
diff --git a/TransactionProcessor.Client/TransactionProcessorClient.cs b/TransactionProcessor.Client/TransactionProcessorClient.cs
index 5b547c70..5c707185 100644
--- a/TransactionProcessor.Client/TransactionProcessorClient.cs
+++ b/TransactionProcessor.Client/TransactionProcessorClient.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Net.Http;
+ using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -12,7 +13,7 @@
///
///
///
- ///
+ ///
///
public class TransactionProcessorClient : ClientProxyBase, ITransactionProcessorClient
{
@@ -48,10 +49,12 @@ public TransactionProcessorClient(Func baseAddressResolver,
///
/// Performs the transaction.
///
+ /// The access token.
/// The transaction request.
/// The cancellation token.
///
- public async Task PerformTransaction(SerialisedMessage transactionRequest,
+ public async Task PerformTransaction(String accessToken,
+ SerialisedMessage transactionRequest,
CancellationToken cancellationToken)
{
SerialisedMessage response = null;
@@ -65,7 +68,7 @@ public async Task PerformTransaction(SerialisedMessage transa
StringContent httpContent = new StringContent(requestSerialised, Encoding.UTF8, "application/json");
// Add the access token to the client headers
- //this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
+ this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Make the Http Call here
HttpResponseMessage httpResponse = await this.HttpClient.PostAsync(requestUri, httpContent, cancellationToken);
diff --git a/TransactionProcessor.DataTransferObjects/LogonTransactionRequest.cs b/TransactionProcessor.DataTransferObjects/LogonTransactionRequest.cs
index d8e58f81..b26a5c66 100644
--- a/TransactionProcessor.DataTransferObjects/LogonTransactionRequest.cs
+++ b/TransactionProcessor.DataTransferObjects/LogonTransactionRequest.cs
@@ -19,22 +19,6 @@ public class LogonTransactionRequest : DataTransferObject
///
public String IMEINumber { get; set; }
- ///
- /// Gets or sets the merchant identifier.
- ///
- ///
- /// The merchant identifier.
- ///
- public Guid MerchantId { get; set; }
-
- ///
- /// Gets or sets the estate identifier.
- ///
- ///
- /// The estate identifier.
- ///
- public Guid EstateId { get; set; }
-
///
/// Gets or sets the transaction date time.
///
diff --git a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs
index 38eb014f..b037b0b2 100644
--- a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs
+++ b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
+ using System.Threading;
using System.Threading.Tasks;
using Client;
using Ductus.FluentDocker.Builders;
@@ -12,29 +13,41 @@
using Ductus.FluentDocker.Services;
using Ductus.FluentDocker.Services.Extensions;
using EstateManagement.Client;
+ using global::Shared.Logger;
+ using SecurityService.Client;
public class DockerHelper
{
+ private readonly NlogLogger Logger;
+
protected INetworkService TestNetwork;
-
+
+ public Int32 SecurityServicePort;
protected Int32 EstateManagementPort;
protected Int32 TransactionProcessorPort;
protected Int32 EventStorePort;
+ public IContainerService SecurityServiceContainer;
public IContainerService EstateManagementContainer;
public IContainerService TransactionProcessorContainer;
protected IContainerService EventStoreContainer;
public IEstateClient EstateClient;
public ITransactionProcessorClient TransactionProcessorClient;
- //public HttpClient HttpClient;
+ public ISecurityServiceClient SecurityServiceClient;
protected String EventStoreConnectionString;
+ public String SecurityServiceContainerName;
protected String EstateManagementContainerName;
protected String TransactionProcessorContainerName;
protected String EventStoreContainerName;
+ public DockerHelper(NlogLogger logger)
+ {
+ this.Logger = logger;
+ }
+
private void SetupTestNetwork()
{
// Build a network
@@ -67,6 +80,7 @@ public async Task StartContainersForScenarioRun(String scenarioName)
this.TestId = testGuid;
// Setup the container names
+ this.SecurityServiceContainerName = $"securityservice{testGuid:N}";
this.EstateManagementContainerName = $"estate{testGuid:N}";
this.TransactionProcessorContainerName = $"txnprocessor{testGuid:N}";
this.EventStoreContainerName = $"eventstore{testGuid:N}";
@@ -75,6 +89,7 @@ public async Task StartContainersForScenarioRun(String scenarioName)
$"EventStoreSettings:ConnectionString=ConnectTo=tcp://admin:changeit@{this.EventStoreContainerName}:1113;VerboseLogging=true;";
this.SetupTestNetwork();
+ this.SetupSecurityServiceContainer(traceFolder);
this.SetupEventStoreContainer(traceFolder);
this.SetupEstateManagementContainer(traceFolder);
this.SetupTransactionProcessorContainer(traceFolder);
@@ -83,14 +98,17 @@ public async Task StartContainersForScenarioRun(String scenarioName)
this.EstateManagementPort = this.EstateManagementContainer.ToHostExposedEndpoint("5000/tcp").Port;
this.TransactionProcessorPort = this.TransactionProcessorContainer.ToHostExposedEndpoint("5002/tcp").Port;
this.EventStorePort = this.EventStoreContainer.ToHostExposedEndpoint("2113/tcp").Port;
+ this.SecurityServicePort = this.SecurityServiceContainer.ToHostExposedEndpoint("5001/tcp").Port;
// Setup the base address resolver
Func estateManagementBaseAddressResolver = api => $"http://127.0.0.1:{this.EstateManagementPort}";
Func transactionProcessorBaseAddressResolver = api => $"http://127.0.0.1:{this.TransactionProcessorPort}";
+ Func securityServiceBaseAddressResolver = api => $"http://127.0.0.1:{this.SecurityServicePort}";
- this.EstateClient = new EstateClient(estateManagementBaseAddressResolver, new HttpClient());
- this.TransactionProcessorClient = new TransactionProcessorClient(transactionProcessorBaseAddressResolver, new HttpClient());
-
+ HttpClient httpClient = new HttpClient();
+ this.EstateClient = new EstateClient(estateManagementBaseAddressResolver, httpClient);
+ this.TransactionProcessorClient = new TransactionProcessorClient(transactionProcessorBaseAddressResolver, httpClient);
+ this.SecurityServiceClient = new SecurityServiceClient(securityServiceBaseAddressResolver, httpClient);
// TODO: Use this to talk to txn processor until we have a client
//this.HttpClient = new HttpClient();
//this.HttpClient.BaseAddress = new Uri(transactionProcessorBaseAddressResolver(String.Empty));
@@ -100,6 +118,13 @@ public async Task StopContainersForScenarioRun()
{
try
{
+ if (this.SecurityServiceContainer != null)
+ {
+ this.SecurityServiceContainer.StopOnDispose = true;
+ this.SecurityServiceContainer.RemoveOnDispose = true;
+ this.SecurityServiceContainer.Dispose();
+ }
+
if (this.TransactionProcessorContainer != null)
{
this.TransactionProcessorContainer.StopOnDispose = true;
@@ -133,6 +158,27 @@ public async Task StopContainersForScenarioRun()
}
}
+ private void SetupSecurityServiceContainer(String traceFolder)
+ {
+ this.Logger.LogInformation("About to Start Security Container");
+
+ this.SecurityServiceContainer = new Builder().UseContainer().WithName(this.SecurityServiceContainerName)
+ .WithEnvironment($"ServiceOptions:PublicOrigin=http://{this.SecurityServiceContainerName}:5001",
+ $"ServiceOptions:IssuerUrl=http://{this.SecurityServiceContainerName}:5001",
+ "ASPNETCORE_ENVIRONMENT=IntegrationTest",
+ "urls=http://*:5001")
+ .WithCredential("https://www.docker.com", "stuartferguson", "Sc0tland")
+ .UseImage("stuartferguson/securityservice").ExposePort(5001).UseNetwork(new List
+ {
+ this.TestNetwork
+ }.ToArray())
+ .Mount(traceFolder, "/home/txnproc/trace", MountType.ReadWrite).Build().Start().WaitForPort("5001/tcp", 30000);
+ Thread.Sleep(20000);
+
+ this.Logger.LogInformation("Security Service Container Started");
+
+ }
+
private void SetupEstateManagementContainer(String traceFolder)
{
// Management API Container
@@ -140,6 +186,8 @@ private void SetupEstateManagementContainer(String traceFolder)
.UseContainer()
.WithName(this.EstateManagementContainerName)
.WithEnvironment(this.EventStoreConnectionString,
+ $"AppSettings:SecurityService=http://{this.SecurityServiceContainerName}:5001",
+ $"SecurityConfiguration:Authority=http://{this.SecurityServiceContainerName}:5001",
"urls=http://*:5000") //,
//"AppSettings:MigrateDatabase=true",
//"EventStoreSettings:START_PROJECTIONS=true",
@@ -159,7 +207,9 @@ private void SetupTransactionProcessorContainer(String traceFolder)
this.TransactionProcessorContainer = new Builder()
.UseContainer()
.WithName(this.TransactionProcessorContainerName)
- .WithEnvironment(this.EventStoreConnectionString) //,
+ .WithEnvironment(this.EventStoreConnectionString,
+ $"AppSettings:SecurityService=http://{this.SecurityServiceContainerName}:5001",
+ $"SecurityConfiguration:Authority=http://{this.SecurityServiceContainerName}:5001") //,
//"AppSettings:MigrateDatabase=true",
//"EventStoreSettings:START_PROJECTIONS=true",
//"EventStoreSettings:ContinuousProjectionsFolder=/app/projections/continuous")
diff --git a/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs b/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs
index ff208e86..af7fc5de 100644
--- a/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs
+++ b/TransactionProcessor.IntegrationTests/Common/GenericSteps.cs
@@ -10,6 +10,8 @@ namespace TransactionProcessor.IntegrationTests.Common
using Ductus.FluentDocker.Extensions;
using Ductus.FluentDocker.Services;
using Ductus.FluentDocker.Services.Extensions;
+ using global::Shared.Logger;
+ using NLog;
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Plugins;
@@ -31,9 +33,18 @@ public GenericSteps(ScenarioContext scenarioContext,
[BeforeScenario]
public async Task StartSystem()
{
+ // Initialise a logger
String scenarioName = this.ScenarioContext.ScenarioInfo.Title.Replace(" ", "");
- this.TestingContext.DockerHelper = new DockerHelper();
+ NlogLogger logger = new NlogLogger();
+ logger.Initialise(LogManager.GetLogger(scenarioName), scenarioName);
+ LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly);
+
+ this.TestingContext.DockerHelper = new DockerHelper(logger);
+ this.TestingContext.Logger = logger;
+ this.TestingContext.Logger.LogInformation("About to Start Containers for Scenario Run");
await this.TestingContext.DockerHelper.StartContainersForScenarioRun(scenarioName).ConfigureAwait(false);
+ this.TestingContext.Logger.LogInformation("Containers for Scenario Run Started");
+
Thread.Sleep(20000);
}
diff --git a/TransactionProcessor.IntegrationTests/Common/TestingContext.cs b/TransactionProcessor.IntegrationTests/Common/TestingContext.cs
index f730e766..4faefd28 100644
--- a/TransactionProcessor.IntegrationTests/Common/TestingContext.cs
+++ b/TransactionProcessor.IntegrationTests/Common/TestingContext.cs
@@ -4,28 +4,252 @@
namespace TransactionProcessor.IntegrationTests.Common
{
+ using System.Linq;
using DataTransferObjects;
+ using global::Shared.Logger;
+ using Shouldly;
+ using TechTalk.SpecFlow;
+
+ //public class TestingContext
+ //{
+ // public TestingContext()
+ // {
+ // this.Estates = new Dictionary();
+ // this.Merchants = new Dictionary();
+ // this.Operators = new Dictionary();
+ // this.EstateMerchants = new Dictionary>();
+ // this.TransactionResponses=new Dictionary();
+ // }
+
+ // public DockerHelper DockerHelper { get; set; }
+
+ // public Dictionary Estates { get; set; }
+ // public Dictionary Merchants { get; set; }
+
+ // public Dictionary Operators { get; set; }
+
+ // public Dictionary> EstateMerchants { get; set; }
+
+ // public Dictionary TransactionResponses { get; set; }
+
+
+ //}
public class TestingContext
{
public TestingContext()
{
- this.Estates = new Dictionary();
+ this.Estates = new List();
+ this.Clients = new List();
+ }
+
+ public NlogLogger Logger { get; set; }
+
+ public DockerHelper DockerHelper { get; set; }
+
+ private List Clients;
+
+ private List Estates;
+
+ public String AccessToken { get; set; }
+
+ public EstateDetails GetEstateDetails(TableRow tableRow)
+ {
+ String estateName = SpecflowTableHelper.GetStringRowValue(tableRow, "EstateName");
+
+ EstateDetails estateDetails = this.Estates.SingleOrDefault(e => e.EstateName == estateName);
+
+ estateDetails.ShouldNotBeNull();
+
+ return estateDetails;
+ }
+
+ public EstateDetails GetEstateDetails(String estateName)
+ {
+ EstateDetails estateDetails = this.Estates.SingleOrDefault(e => e.EstateName == estateName);
+
+ estateDetails.ShouldNotBeNull();
+
+ return estateDetails;
+ }
+
+ public EstateDetails GetEstateDetails(Guid estateId)
+ {
+ EstateDetails estateDetails = this.Estates.SingleOrDefault(e => e.EstateId== estateId);
+
+ estateDetails.ShouldNotBeNull();
+
+ return estateDetails;
+ }
+
+ public void AddEstateDetails(Guid estateId, String estateName)
+ {
+ this.Estates.Add(EstateDetails.Create(estateId, estateName));
+ }
+
+ public void AddClientDetails(String clientId,
+ String clientSecret,
+ String grantType)
+ {
+ this.Clients.Add(ClientDetails.Create(clientId, clientSecret, grantType));
+ }
+
+ public ClientDetails GetClientDetails(String clientId)
+ {
+ ClientDetails clientDetails = this.Clients.SingleOrDefault(c => c.ClientId == clientId);
+
+ clientDetails.ShouldNotBeNull();
+
+ return clientDetails;
+ }
+ }
+
+ public class EstateDetails
+ {
+ private EstateDetails(Guid estateId, String estateName)
+ {
+ this.EstateId = estateId;
+ this.EstateName = estateName;
this.Merchants = new Dictionary();
this.Operators = new Dictionary();
- this.EstateMerchants = new Dictionary>();
- this.TransactionResponses=new Dictionary();
+ this.MerchantUsers = new Dictionary>();
+ this.TransactionResponses = new Dictionary<(Guid merchantId, String transactionNumber), SerialisedMessage>();
}
- public DockerHelper DockerHelper { get; set; }
+ public void AddTransactionResponse(Guid merchantId,
+ String transactionNumber,
+ SerialisedMessage transactionResponse)
+ {
+ this.TransactionResponses.Add((merchantId, transactionNumber), transactionResponse);
+ }
+
+ public SerialisedMessage GetTransactionResponse(Guid merchantId,
+ String transactionNumber)
+ {
+ KeyValuePair<(Guid merchantId, String transactionNumber), SerialisedMessage> transactionResponse =
+ this.TransactionResponses.Where(t => t.Key.merchantId == merchantId && t.Key.transactionNumber == transactionNumber).SingleOrDefault();
+
+ return transactionResponse.Value;
+ }
- public Dictionary Estates { get; set; }
- public Dictionary Merchants { get; set; }
+ private Dictionary<(Guid merchantId, String transactionNumber), SerialisedMessage> TransactionResponses { get; set; }
- public Dictionary Operators { get; set; }
+ public String EstateUser { get; private set; }
+ public String EstatePassword { get; private set; }
- public Dictionary> EstateMerchants { get; set; }
+ public String AccessToken { get; private set; }
+
+ public static EstateDetails Create(Guid estateId,
+ String estateName)
+ {
+ return new EstateDetails(estateId, estateName);
+ }
+
+ public void AddOperator(Guid operatorId,
+ String operatorName)
+ {
+ this.Operators.Add(operatorName, operatorId);
+ }
- public Dictionary TransactionResponses { get; set; }
+ public void AddMerchant(Guid merchantId,
+ String merchantName)
+ {
+ this.Merchants.Add(merchantName, merchantId);
+ }
+
+ public Guid GetMerchantId(String merchantName)
+ {
+ return this.Merchants.Single(m => m.Key == merchantName).Value;
+ }
+
+ public Guid GetOperatorId(String operatorName)
+ {
+ return this.Operators.Single(o => o.Key == operatorName).Value;
+ }
+
+ public void SetEstateUser(String userName,
+ String password)
+ {
+ this.EstateUser = userName;
+ this.EstatePassword = password;
+ }
+
+ public void AddMerchantUser(String merchantName,
+ String userName,
+ String password)
+ {
+ if (this.MerchantUsers.ContainsKey(merchantName))
+ {
+ Dictionary merchantUsersList = this.MerchantUsers[merchantName];
+ if (merchantUsersList.ContainsKey(userName) == false)
+ {
+ merchantUsersList.Add(userName, password);
+ }
+ }
+ else
+ {
+ Dictionary merchantUsersList = new Dictionary();
+ merchantUsersList.Add(userName, password);
+ this.MerchantUsers.Add(merchantName, merchantUsersList);
+ }
+ }
+
+ public void AddMerchantUserToken(String merchantName,
+ String userName,
+ String token)
+ {
+ if (this.MerchantUsersTokens.ContainsKey(merchantName))
+ {
+ Dictionary merchantUsersList = this.MerchantUsersTokens[merchantName];
+ if (merchantUsersList.ContainsKey(userName) == false)
+ {
+ merchantUsersList.Add(userName, token);
+ }
+ }
+ else
+ {
+ Dictionary merchantUsersList = new Dictionary();
+ merchantUsersList.Add(userName, token);
+ this.MerchantUsersTokens.Add(merchantName, merchantUsersList);
+ }
+ }
+
+ public void SetEstateUserToken(String accessToken)
+ {
+ this.AccessToken = accessToken;
+ }
+
+ public Guid EstateId { get; private set; }
+ public String EstateName { get; private set; }
+
+ private Dictionary Operators;
+
+ private Dictionary Merchants;
+
+ private Dictionary> MerchantUsers;
+ private Dictionary> MerchantUsersTokens;
+ }
+
+ public class ClientDetails
+ {
+ public String ClientId { get; private set; }
+ public String ClientSecret { get; private set; }
+ public String GrantType { get; private set; }
+
+ private ClientDetails(String clientId,
+ String clientSecret,
+ String grantType)
+ {
+ this.ClientId = clientId;
+ this.ClientSecret = clientSecret;
+ this.GrantType = grantType;
+ }
+
+ public static ClientDetails Create(String clientId,
+ String clientSecret,
+ String grantType)
+ {
+ return new ClientDetails(clientId, clientSecret, grantType);
+ }
}
}
diff --git a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature
index 7e89f95f..b245bc76 100644
--- a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature
+++ b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature
@@ -2,27 +2,49 @@
Feature: LogonTransaction
Background:
+
+ Given the following api resources exist
+ | ResourceName | DisplayName | Secret | Scopes | UserClaims |
+ | estateManagement | Estate Managememt REST | Secret1 | estateManagement | MerchantId, EstateId, role |
+ | transactionProcessor | Transaction Processor REST | Secret1 | transactionProcessor | |
+
+ Given the following clients exist
+ | ClientId | ClientName | Secret | AllowedScopes | AllowedGrantTypes |
+ | serviceClient | Service Client | Secret1 | estateManagement,transactionProcessor | client_credentials |
+
+ Given I have a token to access the estate management and transaction processor resources
+ | ClientId |
+ | serviceClient |
+
Given I have created the following estates
| EstateName |
| Test Estate 1 |
| Test Estate 2 |
+ Given I have created the following operators
+ | EstateName | OperatorName | RequireCustomMerchantNumber | RequireCustomTerminalNumber |
+ | Test Estate 1 | Test Operator 1 | True | True |
+
Given I create the following merchants
| MerchantName | AddressLine1 | Town | Region | Country | ContactName | EmailAddress | EstateName |
| Test Merchant 1 | Address Line 1 | TestTown | Test Region | United Kingdom | Test Contact 1 | testcontact1@merchant1.co.uk | Test Estate 1 |
| Test Merchant 2 | Address Line 1 | TestTown | Test Region | United Kingdom | Test Contact 2 | testcontact2@merchant2.co.uk | Test Estate 1 |
| Test Merchant 3 | Address Line 1 | TestTown | Test Region | United Kingdom | Test Contact 3 | testcontact3@merchant2.co.uk | Test Estate 2 |
+ Given I have assigned the following operator to the merchants
+ | OperatorName | MerchantName | MerchantNumber | TerminalNumber | EstateName |
+ | Test Operator 1 | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 |
+
@PRTest
Scenario: Logon Transaction
When I perform the following transactions
- | DateTime | TransactionNumber | TransactionType | MerchantName | IMEINumber |
- | Today | 1 | Logon | Test Merchant 1 | 123456789 |
- | Today | 2 | Logon | Test Merchant 2 | 123456789 |
- | Today | 3 | Logon | Test Merchant 3 | 123456789 |
+ | DateTime | TransactionNumber | TransactionType | MerchantName | IMEINumber | EstateName |
+ | Today | 1 | Logon | Test Merchant 1 | 123456789 | Test Estate 1 |
+ | Today | 2 | Logon | Test Merchant 2 | 123456789 | Test Estate 1 |
+ | Today | 3 | Logon | Test Merchant 3 | 123456789 | Test Estate 2 |
Then transaction response should contain the following information
- | TransactionNumber | ResponseCode | ResponseMessage |
- | 1 | 0000 | SUCCESS |
- | 2 | 0000 | SUCCESS |
- | 3 | 0000 | SUCCESS |
+ | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage |
+ | Test Estate 1 | Test Merchant 1 | 1 | 0000 | SUCCESS |
+ | Test Estate 1 | Test Merchant 2 | 2 | 0000 | SUCCESS |
+ | Test Estate 2 | Test Merchant 3 | 3 | 0000 | SUCCESS |
diff --git a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs
index b524a484..b98b05f0 100644
--- a/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs
+++ b/TransactionProcessor.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs
@@ -21,7 +21,7 @@ namespace TransactionProcessor.IntegrationTests.LogonTransaction
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[Xunit.TraitAttribute("Category", "base")]
[Xunit.TraitAttribute("Category", "shared")]
- public partial class LogonTransactionFeature : Xunit.IClassFixture, System.IDisposable
+ public partial class LogonTransactionFeature : object, Xunit.IClassFixture, System.IDisposable
{
private static TechTalk.SpecFlow.ITestRunner testRunner;
@@ -35,7 +35,7 @@ public partial class LogonTransactionFeature : Xunit.IClassFixture estateItem = this.TestingContext.Estates.SingleOrDefault(e => e.Key == estateName);
+ [Given(@"I have assigned the following operator to the merchants")]
+ [When(@"I assign the following operator to the merchants")]
+ public async Task WhenIAssignTheFollowingOperatorToTheMerchants(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow);
- estateItem.Key.ShouldNotBeNullOrEmpty();
- estateItem.Value.ShouldNotBe(Guid.Empty);
+ String token = this.TestingContext.AccessToken;
+ if (String.IsNullOrEmpty(estateDetails.AccessToken) == false)
+ {
+ token = estateDetails.AccessToken;
+ }
- EstateResponse estate = await this.TestingContext.DockerHelper.EstateClient.GetEstate(String.Empty, estateItem.Value, CancellationToken.None).ConfigureAwait(false);
+ // Lookup the merchant id
+ String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName");
+ Guid merchantId = estateDetails.GetMerchantId(merchantName);
+
+ // Lookup the operator id
+ String operatorName = SpecflowTableHelper.GetStringRowValue(tableRow, "OperatorName");
+ Guid operatorId = estateDetails.GetOperatorId(operatorName);
+
+ AssignOperatorRequest assignOperatorRequest = new AssignOperatorRequest
+ {
+ OperatorId = operatorId,
+ MerchantNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantNumber"),
+ TerminalNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "TerminalNumber"),
+ };
+
+ AssignOperatorResponse assignOperatorResponse = await this.TestingContext.DockerHelper.EstateClient.AssignOperatorToMerchant(token, estateDetails.EstateId, merchantId, assignOperatorRequest, CancellationToken.None).ConfigureAwait(false);
+
+ assignOperatorResponse.EstateId.ShouldBe(estateDetails.EstateId);
+ assignOperatorResponse.MerchantId.ShouldBe(merchantId);
+ assignOperatorResponse.OperatorId.ShouldBe(operatorId);
- estate.EstateName.ShouldBe(estateName);
+ this.TestingContext.Logger.LogInformation($"Operator {operatorName} assigned to Estate {estateDetails.EstateName}");
}
}
+
[Given(@"I have created the following operators")]
[When(@"I create the following operators")]
public async Task WhenICreateTheFollowingOperators(Table table)
@@ -90,17 +131,18 @@ public async Task WhenICreateTheFollowingOperators(Table table)
};
// lookup the estate id based on the name in the table
- String estateName = SpecflowTableHelper.GetStringRowValue(tableRow, "EstateName");
- Guid estateId = this.TestingContext.Estates.Single(e => e.Key == estateName).Value;
+ EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow);
- CreateOperatorResponse response = await this.TestingContext.DockerHelper.EstateClient.CreateOperator(String.Empty, estateId, createOperatorRequest, CancellationToken.None).ConfigureAwait(false);
+ CreateOperatorResponse response = await this.TestingContext.DockerHelper.EstateClient.CreateOperator(this.TestingContext.AccessToken, estateDetails.EstateId, createOperatorRequest, CancellationToken.None).ConfigureAwait(false);
response.ShouldNotBeNull();
response.EstateId.ShouldNotBe(Guid.Empty);
response.OperatorId.ShouldNotBe(Guid.Empty);
// Cache the estate id
- this.TestingContext.Operators.Add(operatorName, response.OperatorId);
+ estateDetails.AddOperator(response.OperatorId, operatorName);
+
+ this.TestingContext.Logger.LogInformation($"Operator {operatorName} created with Id {response.OperatorId} for Estate {estateDetails.EstateName}");
}
}
@@ -110,6 +152,14 @@ public async Task WhenICreateTheFollowingMerchants(Table table)
{
foreach (TableRow tableRow in table.Rows)
{
+ // lookup the estate id based on the name in the table
+ EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow);
+ String token = this.TestingContext.AccessToken;
+ if (String.IsNullOrEmpty(estateDetails.AccessToken) == false)
+ {
+ token = estateDetails.AccessToken;
+ }
+
String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName");
CreateMerchantRequest createMerchantRequest = new CreateMerchantRequest
{
@@ -128,83 +178,39 @@ public async Task WhenICreateTheFollowingMerchants(Table table)
}
};
- // lookup the estate id based on the name in the table
- String estateName = SpecflowTableHelper.GetStringRowValue(tableRow, "EstateName");
- Guid estateId = this.TestingContext.Estates.Single(e => e.Key == estateName).Value;
-
CreateMerchantResponse response = await this.TestingContext.DockerHelper.EstateClient
- .CreateMerchant(String.Empty, estateId, createMerchantRequest, CancellationToken.None).ConfigureAwait(false);
+ .CreateMerchant(token, estateDetails.EstateId, createMerchantRequest, CancellationToken.None).ConfigureAwait(false);
response.ShouldNotBeNull();
- response.EstateId.ShouldBe(estateId);
+ response.EstateId.ShouldBe(estateDetails.EstateId);
response.MerchantId.ShouldNotBe(Guid.Empty);
// Cache the merchant id
- this.TestingContext.Merchants.Add(merchantName, response.MerchantId);
- if (this.TestingContext.EstateMerchants.ContainsKey(estateId))
- {
- List merchantIdList = this.TestingContext.EstateMerchants[estateId];
- merchantIdList.Add(response.MerchantId);
- }
- else
- {
- this.TestingContext.EstateMerchants.Add(estateId, new List { response.MerchantId });
- }
- }
+ estateDetails.AddMerchant(response.MerchantId, merchantName);
- foreach (TableRow tableRow in table.Rows)
- {
- String estateName = SpecflowTableHelper.GetStringRowValue(tableRow, "EstateName");
-
- KeyValuePair estateItem = this.TestingContext.Estates.SingleOrDefault(e => e.Key == estateName);
-
- estateItem.Key.ShouldNotBeNullOrEmpty();
- estateItem.Value.ShouldNotBe(Guid.Empty);
-
- String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName");
-
- KeyValuePair merchantItem = this.TestingContext.Merchants.SingleOrDefault(m => m.Key == merchantName);
-
- merchantItem.Key.ShouldNotBeNullOrEmpty();
- merchantItem.Value.ShouldNotBe(Guid.Empty);
-
- MerchantResponse merchant = await this.TestingContext.DockerHelper.EstateClient.GetMerchant(String.Empty, estateItem.Value, merchantItem.Value, CancellationToken.None).ConfigureAwait(false);
-
- merchant.MerchantName.ShouldBe(merchantName);
+ this.TestingContext.Logger.LogInformation($"Merchant {merchantName} created with Id {response.MerchantId} for Estate {estateDetails.EstateName}");
}
- }
- [When(@"I assign the following operator to the merchants")]
- public async Task WhenIAssignTheFollowingOperatorToTheMerchants(Table table)
- {
foreach (TableRow tableRow in table.Rows)
{
- // Lookup the merchant id
- String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName");
- Guid merchantId = this.TestingContext.Merchants[merchantName];
+ EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow);
- // Lookup the operator id
- String operatorName = SpecflowTableHelper.GetStringRowValue(tableRow, "OperatorName");
- Guid operatorId = this.TestingContext.Operators[operatorName];
+ String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName");
- // Now find the estate Id
- Guid estateId = this.TestingContext.EstateMerchants.Where(e => e.Value.Contains(merchantId)).Single().Key;
+ Guid merchantId = estateDetails.GetMerchantId(merchantName);
- AssignOperatorRequest assignOperatorRequest = new AssignOperatorRequest
+ String token = this.TestingContext.AccessToken;
+ if (String.IsNullOrEmpty(estateDetails.AccessToken) == false)
{
- OperatorId = operatorId,
- MerchantNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantNumber"),
- TerminalNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "TerminalNumber"),
- };
+ token = estateDetails.AccessToken;
+ }
- AssignOperatorResponse assignOperatorResponse = await this.TestingContext.DockerHelper.EstateClient.AssignOperatorToMerchant(String.Empty, estateId, merchantId, assignOperatorRequest, CancellationToken.None).ConfigureAwait(false);
+ MerchantResponse merchant = await this.TestingContext.DockerHelper.EstateClient.GetMerchant(token, estateDetails.EstateId, merchantId, CancellationToken.None).ConfigureAwait(false);
- assignOperatorResponse.EstateId.ShouldBe(estateId);
- assignOperatorResponse.MerchantId.ShouldBe(merchantId);
- assignOperatorResponse.OperatorId.ShouldBe(operatorId);
+ merchant.MerchantName.ShouldBe(merchantName);
}
}
-
+
[When(@"I perform the following transactions")]
public async Task WhenIPerformTheFollowingTransactions(Table table)
{
@@ -217,13 +223,15 @@ public async Task WhenIPerformTheFollowingTransactions(Table table)
String transactionType = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionType");
String imeiNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "IMEINumber");
- Guid merchantId = this.TestingContext.Merchants[merchantName];
- Guid estateId = this.TestingContext.EstateMerchants.Single(e => e.Value.Contains(merchantId)).Key;
+ EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow);
+ // Lookup the merchant id
+ Guid merchantId = estateDetails.GetMerchantId(merchantName);
+ SerialisedMessage transactionResponse = null;
switch (transactionType)
{
case "Logon":
- await this.PerformLogonTransaction(estateId,
+ transactionResponse = await this.PerformLogonTransaction(estateDetails.EstateId,
merchantId,
transactionDateTime,
transactionType,
@@ -233,10 +241,12 @@ await this.PerformLogonTransaction(estateId,
break;
}
+
+ estateDetails.AddTransactionResponse(merchantId, transactionNumber, transactionResponse);
}
}
- private async Task PerformLogonTransaction(Guid estateId, Guid merchantId, DateTime transactionDateTime, String transactionType, String transactionNumber, String imeiNumber, CancellationToken cancellationToken)
+ private async Task PerformLogonTransaction(Guid estateId, Guid merchantId, DateTime transactionDateTime, String transactionType, String transactionNumber, String imeiNumber, CancellationToken cancellationToken)
{
LogonTransactionRequest logonTransactionRequest = new LogonTransactionRequest
{
@@ -256,9 +266,12 @@ private async Task PerformLogonTransaction(Guid estateId, Guid merchantId, DateT
TypeNameHandling = TypeNameHandling.All
});
- SerialisedMessage responseSerialisedMessage = await this.TestingContext.DockerHelper.TransactionProcessorClient.PerformTransaction(serialisedMessage, cancellationToken);
-
- this.TestingContext.TransactionResponses.Add(transactionNumber, responseSerialisedMessage);
+ SerialisedMessage responseSerialisedMessage =
+ await this.TestingContext.DockerHelper.TransactionProcessorClient.PerformTransaction(this.TestingContext.AccessToken,
+ serialisedMessage,
+ cancellationToken);
+
+ return responseSerialisedMessage;
}
[Then(@"transaction response should contain the following information")]
@@ -266,8 +279,14 @@ public void ThenTransactionResponseShouldContainTheFollowingInformation(Table ta
{
foreach (TableRow tableRow in table.Rows)
{
+ // Get the merchant name
+ EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow);
+
+ String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName");
+ Guid merchantId = estateDetails.GetMerchantId(merchantName);
+
String transactionNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionNumber");
- SerialisedMessage serialisedMessage = this.TestingContext.TransactionResponses[transactionNumber];
+ SerialisedMessage serialisedMessage = estateDetails.GetTransactionResponse(merchantId, transactionNumber);
Object transactionResponse = JsonConvert.DeserializeObject(serialisedMessage.SerialisedData,
new JsonSerializerSettings
{
@@ -287,7 +306,102 @@ private void ValidateTransactionResponse(LogonTransactionResponse logonTransacti
logonTransactionResponse.ResponseMessage.ShouldBe(expectedResponseMessage);
}
+ [Given(@"the following api resources exist")]
+ public async Task GivenTheFollowingApiResourcesExist(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ String resourceName = SpecflowTableHelper.GetStringRowValue(tableRow, "ResourceName");
+ String displayName = SpecflowTableHelper.GetStringRowValue(tableRow, "DisplayName");
+ String secret = SpecflowTableHelper.GetStringRowValue(tableRow, "Secret");
+ String scopes = SpecflowTableHelper.GetStringRowValue(tableRow, "Scopes");
+ String userClaims = SpecflowTableHelper.GetStringRowValue(tableRow, "UserClaims");
- }
+ List splitScopes = scopes.Split(",").ToList();
+ List splitUserClaims = userClaims.Split(",").ToList();
+ CreateApiResourceRequest createApiResourceRequest = new CreateApiResourceRequest
+ {
+ Description = String.Empty,
+ DisplayName = displayName,
+ Name = resourceName,
+ Scopes = new List(),
+ Secret = secret,
+ UserClaims = new List()
+ };
+ splitScopes.ForEach(a =>
+ {
+ createApiResourceRequest.Scopes.Add(a.Trim());
+ });
+ splitUserClaims.ForEach(a =>
+ {
+ createApiResourceRequest.UserClaims.Add(a.Trim());
+ });
+
+ CreateApiResourceResponse createApiResourceResponse = await this.TestingContext.DockerHelper.SecurityServiceClient.CreateApiResource(createApiResourceRequest, CancellationToken.None).ConfigureAwait(false);
+
+ createApiResourceResponse.ApiResourceName.ShouldBe(resourceName);
+ }
+ }
+
+ [Given(@"the following clients exist")]
+ public async Task GivenTheFollowingClientsExist(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ String clientId = SpecflowTableHelper.GetStringRowValue(tableRow, "ClientId");
+ String clientName = SpecflowTableHelper.GetStringRowValue(tableRow, "ClientName");
+ String secret = SpecflowTableHelper.GetStringRowValue(tableRow, "Secret");
+ String allowedScopes = SpecflowTableHelper.GetStringRowValue(tableRow, "AllowedScopes");
+ String allowedGrantTypes = SpecflowTableHelper.GetStringRowValue(tableRow, "AllowedGrantTypes");
+
+ List splitAllowedScopes = allowedScopes.Split(",").ToList();
+ List splitAllowedGrantTypes = allowedGrantTypes.Split(",").ToList();
+
+ CreateClientRequest createClientRequest = new CreateClientRequest
+ {
+ Secret = secret,
+ AllowedGrantTypes = new List(),
+ AllowedScopes = new List(),
+ ClientDescription = String.Empty,
+ ClientId = clientId,
+ ClientName = clientName
+ };
+
+ splitAllowedScopes.ForEach(a =>
+ {
+ createClientRequest.AllowedScopes.Add(a.Trim());
+ });
+ splitAllowedGrantTypes.ForEach(a =>
+ {
+ createClientRequest.AllowedGrantTypes.Add(a.Trim());
+ });
+
+ CreateClientResponse createClientResponse = await this.TestingContext.DockerHelper.SecurityServiceClient.CreateClient(createClientRequest, CancellationToken.None).ConfigureAwait(false);
+
+ createClientResponse.ClientId.ShouldBe(clientId);
+
+ this.TestingContext.AddClientDetails(clientId, secret, allowedGrantTypes);
+ }
+ }
+
+ [Given(@"I have a token to access the estate management and transaction processor resources")]
+ public async Task GivenIHaveATokenToAccessTheEstateManagementAndTransactionProcessorResources(Table table)
+ {
+ foreach (TableRow tableRow in table.Rows)
+ {
+ String clientId = SpecflowTableHelper.GetStringRowValue(tableRow, "ClientId");
+
+ ClientDetails clientDetails = this.TestingContext.GetClientDetails(clientId);
+
+ if (clientDetails.GrantType == "client_credentials")
+ {
+ TokenResponse tokenResponse = await this.TestingContext.DockerHelper.SecurityServiceClient.GetToken(clientId, clientDetails.ClientSecret, CancellationToken.None).ConfigureAwait(false);
+
+ this.TestingContext.AccessToken = tokenResponse.AccessToken;
+ }
+ }
+ }
+
+ }
}
diff --git a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj
index cead8750..b9f26ce0 100644
--- a/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj
+++ b/TransactionProcessor.IntegrationTests/TransactionProcessor.IntegrationTests.csproj
@@ -7,15 +7,17 @@
-
+
-
+
+
+
-
-
-
+
+
+
all
diff --git a/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj b/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj
index efebda4b..f2c5f8a2 100644
--- a/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj
+++ b/TransactionProcessor.Transaction.DomainEvents/TransactionProcessor.Transaction.DomainEvents.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj b/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj
index 9d386d50..2a87ed14 100644
--- a/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj
+++ b/TransactionProcessor.TransactionAggregate.Tests/TransactionProcessor.TransactionAggregate.Tests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj b/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj
index 02f1e4f2..c0def16c 100644
--- a/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj
+++ b/TransactionProcessor.TransactionAgrgegate/TransactionProcessor.TransactionAggregate.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/TransactionProcessor/Common/ClaimsHelper.cs b/TransactionProcessor/Common/ClaimsHelper.cs
new file mode 100644
index 00000000..f58d03cc
--- /dev/null
+++ b/TransactionProcessor/Common/ClaimsHelper.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace TransactionProcessor.Common
+{
+ using System.Security.Claims;
+ using Shared.Exceptions;
+
+ public class ClaimsHelper
+ {
+ #region Methods
+
+ ///
+ /// Gets the user claims.
+ ///
+ /// The user.
+ /// Type of the custom claim.
+ /// The default value.
+ ///
+ /// No claim [{customClaimType}] found for user id [{userIdClaim.Value}
+ public static Claim GetUserClaim(ClaimsPrincipal user,
+ String customClaimType,
+ String defaultValue = "")
+ {
+ Claim userClaim = null;
+
+ if (ClaimsHelper.IsPasswordToken(user))
+ {
+ // Get the claim from the token
+ userClaim = user.Claims.SingleOrDefault(c => c.Type == customClaimType);
+
+ if (userClaim == null)
+ {
+ throw new NotFoundException($"Claim type [{customClaimType}] not found");
+ }
+ }
+ else
+ {
+ userClaim = new Claim(customClaimType, defaultValue);
+ }
+
+ return userClaim;
+ }
+
+ ///
+ /// Determines whether [is client token] [the specified user].
+ ///
+ /// The user.
+ ///
+ /// true if [is client token] [the specified user]; otherwise, false.
+ ///
+ public static Boolean IsPasswordToken(ClaimsPrincipal user)
+ {
+ Boolean result = false;
+
+ Claim userIdClaim = user.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
+
+ if (userIdClaim != null)
+ {
+ result = true;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Validates the route parameter.
+ ///
+ ///
+ /// The route parameter.
+ /// The user claim.
+ public static Boolean ValidateRouteParameter(T routeParameter,
+ Claim userClaim)
+ {
+ if (routeParameter.ToString() != userClaim.Value)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Determines whether [is user roles valid] [the specified user].
+ ///
+ /// The user.
+ /// The allowed roles.
+ ///
+ /// true if [is user roles valid] [the specified user]; otherwise, false.
+ ///
+ public static Boolean IsUserRolesValid(ClaimsPrincipal user, String[] allowedRoles)
+ {
+ if (IsPasswordToken(user) == false)
+ {
+ return true;
+ }
+
+ foreach (String allowedRole in allowedRoles)
+ {
+ if (user.IsInRole(allowedRole) == false)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ #endregion
+ }
+}
diff --git a/TransactionProcessor/Controllers/TransactionController.cs b/TransactionProcessor/Controllers/TransactionController.cs
index 909bb8b1..96293022 100644
--- a/TransactionProcessor/Controllers/TransactionController.cs
+++ b/TransactionProcessor/Controllers/TransactionController.cs
@@ -5,10 +5,11 @@
using System.Threading;
using System.Threading.Tasks;
using BusinessLogic.Commands;
+ using Common;
using DataTransferObjects;
using Factories;
+ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
- using Microsoft.VisualBasic;
using Newtonsoft.Json;
using Shared.DomainDrivenDesign.CommandHandling;
@@ -20,6 +21,7 @@
[Route(TransactionController.ControllerRoute)]
[ApiController]
[ApiVersion("1.0")]
+ [Authorize]
public class TransactionController : ControllerBase
{
#region Fields
@@ -39,7 +41,7 @@ public class TransactionController : ControllerBase
#region Constructors
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The command router.
/// The model factory.
@@ -51,20 +53,26 @@ public TransactionController(ICommandRouter commandRouter,
}
#endregion
-
+
#region Methods
///
/// Logons the transaction.
///
- /// The logon transaction request.
+ /// The transaction request.
/// The cancellation token.
///
[HttpPost]
[Route("")]
public async Task PerformTransaction([FromBody] SerialisedMessage transactionRequest,
- CancellationToken cancellationToken)
+ CancellationToken cancellationToken)
{
+ // Reject password tokens
+ if (ClaimsHelper.IsPasswordToken(this.User))
+ {
+ return this.Forbid();
+ }
+
Guid estateId = Guid.Parse(transactionRequest.Metadata[MetadataContants.KeyNameEstateId]);
Guid merchantId = Guid.Parse(transactionRequest.Metadata[MetadataContants.KeyNameMerchantId]);
@@ -77,17 +85,24 @@ public async Task PerformTransaction([FromBody] SerialisedMessage
dto.EstateId = estateId;
SerialisedMessage transactionResponse = await this.ProcessSpecificMessage((dynamic)dto, cancellationToken);
-
+
// TODO: Populate the GET route
return this.Created("", transactionResponse);
}
- private async Task ProcessSpecificMessage(LogonTransactionRequest logonTransactionRequest, CancellationToken cancellationToken)
+ ///
+ /// Processes the specific message.
+ ///
+ /// The logon transaction request.
+ /// The cancellation token.
+ ///
+ private async Task ProcessSpecificMessage(LogonTransactionRequest logonTransactionRequest,
+ CancellationToken cancellationToken)
{
Guid transactionId = Guid.NewGuid();
ProcessLogonTransactionCommand command = ProcessLogonTransactionCommand.Create(transactionId,
- logonTransactionRequest.EstateId,
+ logonTransactionRequest.EstateId,
logonTransactionRequest.MerchantId,
logonTransactionRequest.IMEINumber,
logonTransactionRequest.TransactionType,
diff --git a/TransactionProcessor/Startup.cs b/TransactionProcessor/Startup.cs
index ad5a8033..2db7a681 100644
--- a/TransactionProcessor/Startup.cs
+++ b/TransactionProcessor/Startup.cs
@@ -20,6 +20,7 @@ namespace TransactionProcessor
using BusinessLogic.Services;
using Common;
using EventStore.ClientAPI;
+ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.Options;
@@ -32,6 +33,7 @@ namespace TransactionProcessor
using Shared.EventStore.EventStore;
using Shared.Extensions;
using Shared.General;
+ using Shared.Logger;
using Shared.Repositories;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -147,6 +149,28 @@ private void ConfigureMiddlewareServices(IServiceCollection services)
services.AddSwaggerExamplesFromAssemblyOf();
+ services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(options =>
+ {
+ //options.SaveToken = true;
+ options.Authority = ConfigurationReader.GetValue("SecurityConfiguration", "Authority");
+ options.Audience = ConfigurationReader.GetValue("SecurityConfiguration", "ApiName");
+ options.RequireHttpsMetadata = false;
+ options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
+ {
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidAudience = ConfigurationReader.GetValue("SecurityConfiguration", "ApiName"),
+ ValidIssuer = ConfigurationReader.GetValue("SecurityConfiguration", "Authority"),
+ };
+ options.IncludeErrorDetails = true;
+ });
+
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
@@ -187,6 +211,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
app.UseRouting();
+ app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
diff --git a/TransactionProcessor/TransactionProcessor.csproj b/TransactionProcessor/TransactionProcessor.csproj
index 4719e150..551f68c5 100644
--- a/TransactionProcessor/TransactionProcessor.csproj
+++ b/TransactionProcessor/TransactionProcessor.csproj
@@ -7,8 +7,9 @@
-
-
+
+
+
all
@@ -19,7 +20,7 @@
-
+
diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json
index 8fead832..257cac75 100644
--- a/TransactionProcessor/appsettings.json
+++ b/TransactionProcessor/appsettings.json
@@ -19,7 +19,12 @@
"AppSettings": {
"HandlerEventTypesToSilentlyHandle": {
},
- "UseConnectionStringConfig": false
+ "UseConnectionStringConfig": false,
+ "SecurityService": "http://192.168.1.133:5001"
+ },
+ "SecurityConfiguration": {
+ "ApiName": "transactionProcessor",
+ "Authority": "http://192.168.1.133:5001"
},
"AllowedHosts": "*"
}
\ No newline at end of file