Skip to content

Commit

Permalink
Adding Sharding to AuthP
Browse files Browse the repository at this point in the history
  • Loading branch information
JonPSmith committed Mar 23, 2022
1 parent 3f0ed4b commit c4f3034
Show file tree
Hide file tree
Showing 73 changed files with 3,266 additions and 462 deletions.
14 changes: 14 additions & 0 deletions AuthPermissions.AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example3.InvoiceCode", "Exa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example5.MvcWebApp.AzureAdB2C", "Example5.MvcWebApp.AzureAdB2C\Example5.MvcWebApp.AzureAdB2C.csproj", "{8019DAB0-F134-4980-8B1F-0190DF5A41BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example6.SingleLevelSharding", "Example6.SingleLevelSharding\Example6.SingleLevelSharding.csproj", "{9D89C5A6-3692-40A8-A83E-722D99C3004B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -176,6 +178,18 @@ Global
{8019DAB0-F134-4980-8B1F-0190DF5A41BD}.Release|x64.Build.0 = Release|Any CPU
{8019DAB0-F134-4980-8B1F-0190DF5A41BD}.Release|x86.ActiveCfg = Release|Any CPU
{8019DAB0-F134-4980-8B1F-0190DF5A41BD}.Release|x86.Build.0 = Release|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Debug|x64.ActiveCfg = Debug|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Debug|x64.Build.0 = Debug|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Debug|x86.ActiveCfg = Debug|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Debug|x86.Build.0 = Debug|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Release|Any CPU.Build.0 = Release|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Release|x64.ActiveCfg = Release|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Release|x64.Build.0 = Release|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Release|x86.ActiveCfg = Release|Any CPU
{9D89C5A6-3692-40A8-A83E-722D99C3004B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ public interface ILinkToTenantDataService
/// <returns></returns>
string GetDataKeyOfLinkedTenant();

/// <summary>
/// This gets the DataKey and ConnectionName from the <see cref="AccessTenantDataCookie"/>
/// If there no cookie it returns null for both properties
/// </summary>
/// <returns></returns>
/// <exception cref="AuthPermissionsException"></exception>
(string dataKey, string connectionName) GetShardingDataOfLinkedTenant();

/// <summary>
/// This gets the TenantFullName of the tenant that the <see cref="AccessTenantDataCookie"/> contains
/// If there no cookie it returns null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under MIT license. See License.txt in the project root for license information.

using System.Threading.Tasks;
using AuthPermissions.AdminCode;
using AuthPermissions.CommonCode;
using AuthPermissions.DataLayer.Classes;
using AuthPermissions.DataLayer.EfCode;
Expand Down Expand Up @@ -94,13 +95,37 @@ public void StopLinkingToTenant()
/// If there no cookie it returns null
/// </summary>
/// <returns></returns>
/// <exception cref="AuthPermissionsException"></exception>
public string GetDataKeyOfLinkedTenant()
{
if (_options.TenantType.IsSharding())
throw new AuthPermissionsException("You shouldn't be using this method if sharding is turn on");

var cookieValue = _cookieAccessor.GetValue();

return cookieValue == null ? null : DecodeCookieContent(cookieValue).dataKey;
}

/// <summary>
/// This gets the DataKey and ConnectionName from the <see cref="AccessTenantDataCookie"/>
/// If there no cookie it returns null for both properties
/// </summary>
/// <returns></returns>
/// <exception cref="AuthPermissionsException"></exception>
public (string dataKey, string connectionName) GetShardingDataOfLinkedTenant()
{
if (!_options.TenantType.IsSharding())
throw new AuthPermissionsException("You shouldn't be using this method if sharding is turned off");

var cookieValue = _cookieAccessor.GetValue();
if (cookieValue == null)
return (null, null);

var content = DecodeCookieContent(cookieValue);

return (content.dataKey, content.connectionName);
}

/// <summary>
/// This gets the TenantFullName of the tenant that the <see cref="AccessTenantDataCookie"/> contains
/// If there no cookie it returns null
Expand All @@ -119,29 +144,37 @@ public string GetNameOfLinkedTenant()

private string EncodeCookieContent(Tenant tenantToLinkToTenant)
{
//thanks to https://stackoverflow.com/questions/13254211/how-to-convert-string-to-datetime-as-utc-as-simple-as-that
//var threeValues = $"{tenantToLinkToTenant.GetTenantDataKey()},{DateTime.UtcNow.ToShortTimeString()},{tenantToLinkToTenant.TenantFullName}";
var twoValues = $"{tenantToLinkToTenant.GetTenantDataKey()},{tenantToLinkToTenant.TenantFullName}";
var values = _options.TenantType.IsSharding()
? $"{tenantToLinkToTenant.GetTenantDataKey()},{tenantToLinkToTenant.ConnectionName},{tenantToLinkToTenant.TenantFullName}"
: $"{tenantToLinkToTenant.GetTenantDataKey()},{tenantToLinkToTenant.TenantFullName}";

return _encryptorService.Encrypt(twoValues);
return _encryptorService.Encrypt(values);
}

private (string dataKey, string tenantName) DecodeCookieContent(string cookieValue)
private (string dataKey, string tenantName, string connectionName) DecodeCookieContent(string cookieValue)
{
string twoValues;
string values;
try
{
twoValues = _encryptorService.Decrypt(cookieValue);
values = _encryptorService.Decrypt(cookieValue);
}
catch
{
throw new AuthPermissionsException("The content of the Access Tenant Data cookie was bad.");
}

var firstComma = twoValues.IndexOf(',');
var firstComma = values.IndexOf(',');
if (firstComma == -1)
throw new AuthPermissionsException("Could not find the user you were looking for.");

return (twoValues.Substring(0, firstComma), twoValues.Substring(firstComma + 1));
//without sharding
if (!_options.TenantType.HasFlag(TenantTypes.AddSharding))
return (values.Substring(0, firstComma), values.Substring(firstComma + 1), null);

//with sharding (order is DataKey, ConnectionName, Tenant name - this overcomes the problem of commas in the tenant name
var secondComma = values.Substring(firstComma + 1).IndexOf(',')+ firstComma + 1;
return (values.Substring(0, firstComma),
values.Substring(secondComma + 1),
values.Substring(firstComma + 1, secondComma - firstComma - 1 ));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace AuthPermissions.AspNetCore.GetDataKeyCode
{

/// <summary>
/// This service is registered if a multi-tenant setup is defined <see cref="AuthPermissionsOptions.TenantType"/>
/// NOTE: There is a <see cref="GetDataKeyFromUserAccessTenantData"/> version if the "Access the data of other tenant" is turned on
/// This service is registered if a multi-tenant setup without sharding
/// NOTE: There are other version if the "Access the data of other tenant" is turned on
/// </summary>
public class GetDataKeyFromUserNormal : IGetDataKeyFromUser
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2022 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
// Licensed under MIT license. See License.txt in the project root for license information.

using AuthPermissions.AspNetCore.AccessTenantData;
using AuthPermissions.AspNetCore.Services;
using AuthPermissions.CommonCode;
using Microsoft.AspNetCore.Http;

namespace AuthPermissions.AspNetCore.GetDataKeyCode
{

/// <summary>
/// This service is registered if a multi-tenant setup with sharding on
/// NOTE: There are other versions if the "Access the data of other tenant" is turned on
/// </summary>
public class GetShardingDataAppAndHierarchicalUsersAccessTenantData : IGetShardingDataFromUser
{
/// <summary>
/// This will return the AuthP's DataKey and the connection string via the ConnectionName claim.
/// This version works with tenant users, but is little bit slower than the version that only works with app users
/// If no user, or no claim then both parameters will be null
/// </summary>
/// <param name="accessor"></param>
/// <param name="connectionService">Service to get the current connection string for the </param>
/// <param name="linkService"></param>
public GetShardingDataAppAndHierarchicalUsersAccessTenantData(IHttpContextAccessor accessor,
IShardingConnections connectionService,
ILinkToTenantDataService linkService)
{
var overrideLink = linkService.GetShardingDataOfLinkedTenant();

DataKey = overrideLink.dataKey ?? accessor.HttpContext?.User.GetAuthDataKeyFromUser();

var connectionStringName = overrideLink.connectionName
?? accessor.HttpContext?.User.GetConnectionNameFromUser();

if (connectionStringName != null)
ConnectionString = connectionService.GetNamedConnectionString(connectionStringName);
}

/// <summary>
/// The AuthP' DataKey, can be null.
/// </summary>
public string DataKey { get; }

/// <summary>
/// This contains the connection string to the database to use
/// If null, then use the default connection string as defined at the time when your application's DbContext was registered
/// </summary>
public string ConnectionString { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2022 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
// Licensed under MIT license. See License.txt in the project root for license information.

using AuthPermissions.AspNetCore.AccessTenantData;
using AuthPermissions.AspNetCore.Services;
using AuthPermissions.CommonCode;
using Microsoft.AspNetCore.Http;

namespace AuthPermissions.AspNetCore.GetDataKeyCode
{

/// <summary>
/// This service is registered if a multi-tenant setup with sharding on
/// NOTE: There are other versions if the "Access the data of other tenant" is turned on
/// </summary>
public class GetShardingDataUserAccessTenantData : IGetShardingDataFromUser
{
/// <summary>
/// This will return the AuthP's DataKey and the connection string via the ConnectionName claim,
/// but only if the user doesn't have a tenant, i.e. an app admin user
/// If no user, or no claim then both parameters will be null
/// </summary>
/// <param name="accessor"></param>
/// <param name="connectionService">Service to get the current connection string for the </param>
/// <param name="linkService"></param>
public GetShardingDataUserAccessTenantData(IHttpContextAccessor accessor,
IShardingConnections connectionService,
ILinkToTenantDataService linkService)
{
var overrideLink = linkService.GetShardingDataOfLinkedTenant();

DataKey = accessor.HttpContext?.User.GetAuthDataKeyFromUser()
?? overrideLink.dataKey;
var connectionStringName = accessor.HttpContext?.User.GetConnectionNameFromUser()
?? overrideLink.connectionName;

if (connectionStringName != null)
ConnectionString = connectionService.GetNamedConnectionString(connectionStringName);
}

/// <summary>
/// The AuthP' DataKey, can be null.
/// </summary>
public string DataKey { get; }

/// <summary>
/// This contains the connection string to the database to use
/// If null, then use the default connection string as defined at the time when your application's DbContext was registered
/// </summary>
public string ConnectionString { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2022 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
// Licensed under MIT license. See License.txt in the project root for license information.

using AuthPermissions.AspNetCore.Services;
using AuthPermissions.CommonCode;
using Microsoft.AspNetCore.Http;

namespace AuthPermissions.AspNetCore.GetDataKeyCode
{

/// <summary>
/// This service is registered if a multi-tenant setup with sharding on
/// NOTE: There are other versions if the "Access the data of other tenant" is turned on
/// </summary>
public class GetShardingDataUserNormal : IGetShardingDataFromUser
{
/// <summary>
/// This will return the AuthP's DataKey and the connection string via the ConnectionName claim.
/// If no user, or no claim then both parameters will be null
/// </summary>
/// <param name="accessor"></param>
/// <param name="connectionService">Service to get the current connection string for the </param>
public GetShardingDataUserNormal(IHttpContextAccessor accessor, IShardingConnections connectionService)
{
DataKey = accessor.HttpContext?.User.GetAuthDataKeyFromUser();
var connectionStringName = accessor.HttpContext?.User.GetConnectionNameFromUser();
if (connectionStringName != null)
ConnectionString = connectionService.GetNamedConnectionString(connectionStringName);
}

/// <summary>
/// The AuthP' DataKey, can be null.
/// </summary>
public string DataKey { get; }

/// <summary>
/// This contains the connection string to the database to use
/// If null, then use the default connection string as defined at the time when your application's DbContext was registered
/// </summary>
public string ConnectionString { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2022 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
// Licensed under MIT license. See License.txt in the project root for license information.

namespace AuthPermissions.AspNetCore.GetDataKeyCode;

/// <summary>
/// This is the interface provides both the DataKey and the connection string
/// </summary>
public interface IGetShardingDataFromUser
{
/// <summary>
/// The DataKey to be used for multi-tenant applications
/// </summary>
string DataKey { get; }

/// <summary>
/// This contains the connection string to the database to use
/// If null, then use the default connection string as defined at the time when your application's DbContext was registered
/// </summary>
string ConnectionString { get; }

}
25 changes: 25 additions & 0 deletions AuthPermissions.AspNetCore/Services/IShardingConnections.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2022 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
// Licensed under MIT license. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace AuthPermissions.AspNetCore.Services;

/// <summary>
/// The interface for the service to manage the connection strings in the appsetting file.
/// </summary>
public interface IShardingConnections
{
/// <summary>
/// This returns all the connection strings name in the application's appsettings
/// </summary>
/// <returns>The name of each connection string</returns>
IEnumerable<string> GetAllConnectionStringNames();

/// <summary>
/// This will provide the connection string for the entry with the given connection string name
/// </summary>
/// <param name="connectionName">The name of the connection string you want to access</param>
/// <returns>The connection string, or null if not found</returns>
string GetNamedConnectionString(string connectionName);
}
Loading

0 comments on commit c4f3034

Please sign in to comment.