Replaced custom DataProtection.Redis lib with official Microsoft pkg
Removed Sessions in WebMVC app Fixed GracePeriodMgr issues using multiple instances
This commit is contained in:
parent
c3d9deb030
commit
4dff728d11
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2002
|
||||
VisualStudioVersion = 15.0.27004.2009
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}"
|
||||
EndProject
|
||||
@ -83,10 +83,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Location", "Location", "{41
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Locations.API", "src\Services\Location\Locations.API\Locations.API.csproj", "{E7581357-FC34-474C-B8F5-307EE3CE05EF}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataProtection", "DataProtection", "{88B22DBB-AA8F-4290-A454-2C109352C345}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataProtection", "src\BuildingBlocks\DataProtection\DataProtection\DataProtection.csproj", "{23A33F9B-7672-426D-ACF9-FF8436ADC81A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Marketing", "Marketing", "{A5260DE0-1FDD-467E-9CC1-A028AB081CEE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.API", "src\Services\Marketing\Marketing.API\Marketing.API.csproj", "{DF395F85-B010-465D-857A-7EBCC512C0C2}"
|
||||
@ -1075,54 +1071,6 @@ Global
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
@ -1402,8 +1350,6 @@ Global
|
||||
{4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
||||
{41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6}
|
||||
{88B22DBB-AA8F-4290-A454-2C109352C345} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A} = {88B22DBB-AA8F-4290-A454-2C109352C345}
|
||||
{A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
||||
{DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE}
|
||||
{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
|
||||
|
@ -1,13 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.5</TargetFramework>
|
||||
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="1.1.2" />
|
||||
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,97 +0,0 @@
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks
|
||||
{
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IDataProtectionBuilder"/> for configuring
|
||||
/// data protection options.
|
||||
/// </summary>
|
||||
public static class DataProtectionBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets up data protection to persist session keys in Redis.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> used to set up data protection options.</param>
|
||||
/// <param name="redisConnectionString">The connection string specifying the Redis instance and database for key storage.</param>
|
||||
/// <returns>
|
||||
/// The <paramref name="builder" /> for continued configuration.
|
||||
/// </returns>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown if <paramref name="builder" /> or <paramref name="redisConnectionString" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// Thrown if <paramref name="redisConnectionString" /> is empty.
|
||||
/// </exception>
|
||||
public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, string redisConnectionString)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (redisConnectionString == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(redisConnectionString));
|
||||
}
|
||||
|
||||
if (redisConnectionString.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString));
|
||||
}
|
||||
|
||||
var configuration = ConfigurationOptions.Parse(redisConnectionString, true);
|
||||
configuration.ResolveDns = true;
|
||||
|
||||
return builder.Use(ServiceDescriptor.Singleton<IXmlRepository>(services =>
|
||||
new RedisXmlRepository(configuration, services.GetRequiredService<ILogger<RedisXmlRepository>>())));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an <see cref="IDataProtectionBuilder"/> to use the service of
|
||||
/// a specific type, removing all other services of that type.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> that should use the specified service.</param>
|
||||
/// <param name="descriptor">The <see cref="ServiceDescriptor"/> with the service the <paramref name="builder" /> should use.</param>
|
||||
/// <returns>
|
||||
/// The <paramref name="builder" /> for continued configuration.
|
||||
/// </returns>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown if <paramref name="builder" /> or <paramref name="descriptor" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public static IDataProtectionBuilder Use(this IDataProtectionBuilder builder, ServiceDescriptor descriptor)
|
||||
{
|
||||
// This algorithm of removing all other services of a specific type
|
||||
// before adding the new/replacement service is how the base ASP.NET
|
||||
// DataProtection bits work. Due to some of the differences in how
|
||||
// that base set of bits handles DI, it's better to follow suit
|
||||
// and work in the same way than to try and debug weird issues.
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (descriptor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(descriptor));
|
||||
}
|
||||
|
||||
for (int i = builder.Services.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (builder.Services[i]?.ServiceType == descriptor.ServiceType)
|
||||
{
|
||||
builder.Services.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
builder.Services.Add(descriptor);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks
|
||||
{
|
||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// Key repository that stores XML encrypted keys in a Redis distributed cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The values stored in Redis are XML documents that contain encrypted session
|
||||
/// keys used for the protection of things like session state. The document contents
|
||||
/// are double-encrypted - first with a changing session key; then by a master key.
|
||||
/// As such, there's no risk in storing the keys in Redis - even if someone can crack
|
||||
/// the master key, they still need to also crack the session key. (Other solutions
|
||||
/// for sharing keys across a farm environment include writing them to files
|
||||
/// on a file share.)
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// While the repository uses a hash to keep the set of encrypted keys separate, you
|
||||
/// can further separate these items from other items in Redis by specifying a unique
|
||||
/// database in the connection string.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Consumers of the repository are responsible for caching the XML items as needed.
|
||||
/// Typically repositories are consumed by things like <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider"/>
|
||||
/// which generates <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.CacheableKeyRing"/>
|
||||
/// values that get cached. The mechanism is already optimized for caching so there's
|
||||
/// no need to create a redundant cache.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository" />
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
public class RedisXmlRepository : IXmlRepository, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The root cache key for XML items stored in Redis
|
||||
/// </summary>
|
||||
public static readonly string RedisHashKey = "DataProtectionXmlRepository";
|
||||
|
||||
/// <summary>
|
||||
/// The connection to the Redis backing store.
|
||||
/// </summary>
|
||||
private IConnectionMultiplexer _connection;
|
||||
|
||||
/// <summary>
|
||||
/// Flag indicating whether the object has been disposed.
|
||||
/// </summary>
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="connectionString">
|
||||
/// The Redis connection string.
|
||||
/// </param>
|
||||
/// <param name="logger">
|
||||
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown if <paramref name="connectionString" /> or <paramref name="logger" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public RedisXmlRepository(ConfigurationOptions connectionString, ILogger<RedisXmlRepository> logger)
|
||||
: this(ConnectionMultiplexer.Connect(connectionString), logger)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="connection">
|
||||
/// The Redis database connection.
|
||||
/// </param>
|
||||
/// <param name="logger">
|
||||
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown if <paramref name="connection" /> or <paramref name="logger" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public RedisXmlRepository(IConnectionMultiplexer connection, ILogger<RedisXmlRepository> logger)
|
||||
{
|
||||
if (connection == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(connection));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
this._connection = connection;
|
||||
this.Logger = logger;
|
||||
|
||||
// Mask the password so it doesn't get logged.
|
||||
var configuration = Regex.Replace(this._connection.Configuration, @"password\s*=\s*[^,]*", "password=****", RegexOptions.IgnoreCase);
|
||||
this.Logger.LogDebug("Storing data protection keys in Redis: {RedisConfiguration}", configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
|
||||
/// </value>
|
||||
public ILogger<RedisXmlRepository> Logger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing,
|
||||
/// or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all top-level XML elements in the repository.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="IReadOnlyCollection{T}"/> with the set of elements
|
||||
/// stored in the repository.
|
||||
/// </returns>
|
||||
public IReadOnlyCollection<XElement> GetAllElements()
|
||||
{
|
||||
var database = this._connection.GetDatabase();
|
||||
var hash = database.HashGetAll(RedisHashKey);
|
||||
var elements = new List<XElement>();
|
||||
|
||||
if (hash == null || hash.Length == 0)
|
||||
{
|
||||
return elements.AsReadOnly();
|
||||
}
|
||||
|
||||
foreach (var item in hash.ToStringDictionary())
|
||||
{
|
||||
elements.Add(XElement.Parse(item.Value));
|
||||
}
|
||||
|
||||
this.Logger.LogDebug("Read {XmlElementCount} XML elements from Redis.", elements.Count);
|
||||
return elements.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a top-level XML element to the repository.
|
||||
/// </summary>
|
||||
/// <param name="element">The element to add.</param>
|
||||
/// <param name="friendlyName">
|
||||
/// An optional name to be associated with the XML element.
|
||||
/// For instance, if this repository stores XML files on disk, the friendly name may
|
||||
/// be used as part of the file name. Repository implementations are not required to
|
||||
/// observe this parameter even if it has been provided by the caller.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// The <paramref name="friendlyName" /> parameter must be unique if specified.
|
||||
/// For instance, it could be the ID of the key being stored.
|
||||
/// </remarks>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown if <paramref name="element" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public void StoreElement(XElement element, string friendlyName)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(friendlyName))
|
||||
{
|
||||
// The framework always passes in a name, but
|
||||
// the contract indicates this may be null or empty.
|
||||
friendlyName = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
this.Logger.LogDebug("Storing XML element with friendly name {XmlElementFriendlyName}.", friendlyName);
|
||||
|
||||
this._connection.GetDatabase().HashSet(RedisHashKey, friendlyName, element.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!this._disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (this._connection != null)
|
||||
{
|
||||
this._connection.Close();
|
||||
this._connection.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
this._connection = null;
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -59,6 +59,12 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
||||
guid : basketCheckout.RequestId;
|
||||
|
||||
var basket = await _repository.GetBasketAsync(userId);
|
||||
|
||||
if (basket == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, basketCheckout.City, basketCheckout.Street,
|
||||
basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName,
|
||||
basketCheckout.CardExpiration, basketCheckout.CardSecurityNumber, basketCheckout.CardTypeId, basketCheckout.Buyer, basketCheckout.RequestId, basket);
|
||||
@ -66,12 +72,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
||||
// Once basket is checkout, sends an integration event to
|
||||
// ordering.api to convert basket to order and proceeds with
|
||||
// order creation process
|
||||
_eventBus.Publish(eventMessage);
|
||||
|
||||
if (basket == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
_eventBus.Publish(eventMessage);
|
||||
|
||||
return Accepted();
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.0.0" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -43,7 +44,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\BuildingBlocks\DataProtection\DataProtection\DataProtection.csproj" />
|
||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
|
||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||
|
@ -4,10 +4,10 @@ using IdentityServer4.Services;
|
||||
using Microsoft.ApplicationInsights.Extensibility;
|
||||
using Microsoft.ApplicationInsights.ServiceFabric;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks;
|
||||
using Microsoft.eShopOnContainers.Services.Identity.API.Certificates;
|
||||
using Microsoft.eShopOnContainers.Services.Identity.API.Data;
|
||||
using Microsoft.eShopOnContainers.Services.Identity.API.Models;
|
||||
@ -16,6 +16,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
@ -59,7 +60,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
|
||||
{
|
||||
opts.ApplicationDiscriminator = "eshop.identity";
|
||||
})
|
||||
.PersistKeysToRedis(Configuration["DPConnectionString"]);
|
||||
.PersistKeysToRedis(ConnectionMultiplexer.Connect(Configuration["DPConnectionString"]), "DataProtection-Keys");
|
||||
}
|
||||
|
||||
services.AddHealthChecks(checks =>
|
||||
|
@ -86,9 +86,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
[ProducesResponseType(typeof(IEnumerable<OrderSummary>), (int)HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetOrders()
|
||||
{
|
||||
var orderTask = _orderQueries.GetOrdersAsync();
|
||||
|
||||
var orders = await orderTask;
|
||||
var orders = await _orderQueries.GetOrdersAsync();
|
||||
|
||||
return Ok(orders);
|
||||
}
|
||||
|
@ -92,42 +92,34 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
|
||||
}
|
||||
|
||||
public void SetAwaitingValidationStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.Cancelled.Id ||
|
||||
_orderStatusId != OrderStatus.Submitted.Id)
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.Submitted.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.AwaitingValidation);
|
||||
}
|
||||
|
||||
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
|
||||
|
||||
_orderStatusId = OrderStatus.AwaitingValidation.Id;
|
||||
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
|
||||
_orderStatusId = OrderStatus.AwaitingValidation.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStockConfirmedStatus()
|
||||
{
|
||||
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
|
||||
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.StockConfirmed);
|
||||
}
|
||||
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
|
||||
|
||||
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
|
||||
|
||||
_orderStatusId = OrderStatus.StockConfirmed.Id;
|
||||
_description = "All the items were confirmed with available stock.";
|
||||
_orderStatusId = OrderStatus.StockConfirmed.Id;
|
||||
_description = "All the items were confirmed with available stock.";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPaidStatus()
|
||||
{
|
||||
if (_orderStatusId != OrderStatus.StockConfirmed.Id)
|
||||
if (_orderStatusId == OrderStatus.StockConfirmed.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.Paid);
|
||||
}
|
||||
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
|
||||
|
||||
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
|
||||
|
||||
_orderStatusId = OrderStatus.Paid.Id;
|
||||
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
|
||||
_orderStatusId = OrderStatus.Paid.Id;
|
||||
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShippedStatus()
|
||||
@ -155,19 +147,17 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
|
||||
|
||||
public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
|
||||
{
|
||||
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
|
||||
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.Cancelled);
|
||||
}
|
||||
_orderStatusId = OrderStatus.Cancelled.Id;
|
||||
|
||||
_orderStatusId = OrderStatus.Cancelled.Id;
|
||||
var itemsStockRejectedProductNames = OrderItems
|
||||
.Where(c => orderStockRejectedItems.Contains(c.ProductId))
|
||||
.Select(c => c.GetOrderItemProductName());
|
||||
|
||||
var itemsStockRejectedProductNames = OrderItems
|
||||
.Where(c => orderStockRejectedItems.Contains(c.ProductId))
|
||||
.Select(c => c.GetOrderItemProductName());
|
||||
|
||||
var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames);
|
||||
_description = $"The product items don't have stock: ({itemsStockRejectedDescription}).";
|
||||
var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames);
|
||||
_description = $"The product items don't have stock: ({itemsStockRejectedDescription}).";
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOrderStartedDomainEvent(string userId, int cardTypeId, string cardNumber,
|
||||
|
@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.eShopOnContainers.WebMVC.Services;
|
||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Polly.CircuitBreaker;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
||||
{
|
||||
@ -94,12 +92,12 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
||||
HandleBrokenCircuitException();
|
||||
}
|
||||
|
||||
return RedirectToAction("Index", "Catalog");
|
||||
return RedirectToAction("Index", "Catalog", new { errorMsg = ViewBag.BasketInoperativeMsg });
|
||||
}
|
||||
|
||||
private void HandleBrokenCircuitException()
|
||||
{
|
||||
TempData["BasketInoperativeMsg"] = "Basket Service is inoperative, please try later on. (Business Msg Due to Circuit-Breaker)";
|
||||
ViewBag.BasketInoperativeMsg = "Basket Service is inoperative, please try later on. (Business Msg Due to Circuit-Breaker)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels.Pagination;
|
||||
using Microsoft.eShopOnContainers.WebMVC.Services;
|
||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels.CatalogViewModels;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
||||
{
|
||||
@ -14,7 +15,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
||||
public CatalogController(ICatalogService catalogSvc) =>
|
||||
_catalogSvc = catalogSvc;
|
||||
|
||||
public async Task<IActionResult> Index(int? BrandFilterApplied, int? TypesFilterApplied, int? page)
|
||||
public async Task<IActionResult> Index(int? BrandFilterApplied, int? TypesFilterApplied, int? page, [FromQuery]string errorMsg)
|
||||
{
|
||||
var itemsPage = 10;
|
||||
var catalog = await _catalogSvc.GetCatalogItems(page ?? 0, itemsPage, BrandFilterApplied, TypesFilterApplied);
|
||||
@ -37,6 +38,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
||||
vm.PaginationInfo.Next = (vm.PaginationInfo.ActualPage == vm.PaginationInfo.TotalPages - 1) ? "is-disabled" : "";
|
||||
vm.PaginationInfo.Previous = (vm.PaginationInfo.ActualPage == 0) ? "is-disabled" : "";
|
||||
|
||||
ViewBag.BasketInoperativeMsg = errorMsg;
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ using Microsoft.ApplicationInsights.ServiceFabric;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||
using Microsoft.eShopOnContainers.WebMVC.Infrastructure;
|
||||
using Microsoft.eShopOnContainers.WebMVC.Services;
|
||||
@ -14,6 +14,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using WebMVC.Infrastructure;
|
||||
@ -36,7 +37,8 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
||||
{
|
||||
RegisterAppInsights(services);
|
||||
|
||||
services.AddMvc();
|
||||
services.AddMvc();
|
||||
|
||||
services.AddSession();
|
||||
|
||||
if (Configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
|
||||
@ -45,7 +47,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
||||
{
|
||||
opts.ApplicationDiscriminator = "eshop.webmvc";
|
||||
})
|
||||
.PersistKeysToRedis(Configuration["DPConnectionString"]);
|
||||
.PersistKeysToRedis(ConnectionMultiplexer.Connect(Configuration["DPConnectionString"]), "DataProtection-Keys");
|
||||
}
|
||||
|
||||
services.Configure<AppSettings>(Configuration);
|
||||
|
@ -24,9 +24,9 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents
|
||||
return View(vm);
|
||||
}
|
||||
catch (BrokenCircuitException)
|
||||
{
|
||||
{
|
||||
// Catch error when Basket.api is in circuit-opened mode
|
||||
TempData["BasketInoperativeMsg"] = "Basket Service is inoperative, please try later on. (Business Msg Due to Circuit-Breaker)";
|
||||
ViewBag.BasketInoperativeMsg = "Basket Service is inoperative, please try later on. (Business Msg Due to Circuit-Breaker)";
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
|
@ -25,10 +25,10 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<br />
|
||||
@if(TempData.ContainsKey("BasketInoperativeMsg"))
|
||||
@if(ViewBag.BasketInoperativeMsg != null)
|
||||
{
|
||||
<div class="alert alert-warning" role="alert">
|
||||
@TempData["BasketInoperativeMsg"]
|
||||
@ViewBag.BasketInoperativeMsg
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -5,21 +5,21 @@
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
@if (TempData.ContainsKey("BasketInoperativeMsg"))
|
||||
@if (ViewBag.BasketInoperativeMsg != null)
|
||||
{
|
||||
<br />
|
||||
<div class="alert alert-warning" role="alert">
|
||||
@TempData["BasketInoperativeMsg"]
|
||||
@ViewBag.BasketInoperativeMsg
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<article class="esh-basket-titles row">
|
||||
<br />
|
||||
@if (TempData.ContainsKey("BasketInoperativeMsg"))
|
||||
@if (ViewBag.BasketInoperativeMsg != null)
|
||||
{
|
||||
<div class="alert alert-warning" role="alert">
|
||||
@TempData["BasketInoperativeMsg"]
|
||||
@ViewBag.BasketInoperativeMsg
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta1" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.0-beta1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Fabric.MSBuild" Version="1.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -39,7 +40,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\BuildingBlocks\DataProtection\DataProtection\DataProtection.csproj" />
|
||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||
<ProjectReference Include="..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
|
||||
|
@ -3,13 +3,14 @@ using Microsoft.ApplicationInsights.Extensibility;
|
||||
using Microsoft.ApplicationInsights.ServiceFabric;
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.IO;
|
||||
using WebSPA.Infrastructure;
|
||||
@ -64,7 +65,7 @@ namespace eShopConContainers.WebSPA
|
||||
{
|
||||
opts.ApplicationDiscriminator = "eshop.webspa";
|
||||
})
|
||||
.PersistKeysToRedis(Configuration["DPConnectionString"]);
|
||||
.PersistKeysToRedis(ConnectionMultiplexer.Connect(Configuration["DPConnectionString"]), "DataProtection-Keys");
|
||||
}
|
||||
|
||||
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
|
||||
|
@ -32,6 +32,7 @@
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta1" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.0-beta1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -61,7 +62,6 @@
|
||||
-->
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\BuildingBlocks\DataProtection\DataProtection\DataProtection.csproj" />
|
||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||
</ItemGroup>
|
||||
@ -76,4 +76,6 @@
|
||||
<Folder Include="wwwroot\assets\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties package-lock_1json__JSONSchema="http://json.schemastore.org/bower" /></VisualStudio></ProjectExtensions>
|
||||
|
||||
</Project>
|
||||
|
@ -44,7 +44,7 @@ namespace UnitTest.Catalog.Application
|
||||
|
||||
//Act
|
||||
var orderController = new CatalogController(_catalogServiceMock.Object);
|
||||
var actionResult = await orderController.Index(fakeBrandFilterApplied, fakeTypesFilterApplied, fakePage);
|
||||
var actionResult = await orderController.Index(fakeBrandFilterApplied, fakeTypesFilterApplied, fakePage, null);
|
||||
|
||||
//Assert
|
||||
var viewResult = Assert.IsType<ViewResult>(actionResult);
|
||||
|
Loading…
x
Reference in New Issue
Block a user