Skip to content

Commit

Permalink
Merge pull request #15 from jabbera/cacheAdResults
Browse files Browse the repository at this point in the history
Cache AD results for performance
  • Loading branch information
jabbera authored May 3, 2017
2 parents e9f468d + f368629 commit 81921b0
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 81 deletions.
17 changes: 7 additions & 10 deletions RutaHttpModule/AdInteraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,21 @@ internal AdInteraction()

string usernameOnly = domainUsername.RemoveDomain();

string login = null, name = null, email = null;
string[] groups = null;

using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, usernameOnly))
{
if (user?.DistinguishedName.EndsWith(this.settings.AdUserBaseDn, StringComparison.OrdinalIgnoreCase) != true)
{
return (login, name, email, groups);
return (null, null, null, null);
}

login = usernameOnly;
name = user.Name;
email = user.EmailAddress;
groups = user.GetGroupsFast(this.settings.AdUserBaseDn, this.settings.AdGroupBaseDn).ToArray();
}
string login = usernameOnly;
string name = user.Name;
string email = user.EmailAddress;
string[] groups = user.GetGroupsFast(this.settings.AdUserBaseDn, this.settings.AdGroupBaseDn).ToArray();

return (login, name, email, groups);
return (login, name, email, groups);
}
}
}
}
12 changes: 5 additions & 7 deletions RutaHttpModule/RutaHttpModule.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
Expand Down Expand Up @@ -74,13 +70,15 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.ValueTuple">
<Version>4.3.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
68 changes: 46 additions & 22 deletions RutaHttpModule/RutaModule.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace RutaHttpModule
using System.Collections.Concurrent;

namespace RutaHttpModule
{
using System;
using System.Diagnostics;
Expand Down Expand Up @@ -30,6 +32,9 @@ public sealed class RutaModule : IHttpModule
/// </summary>
private readonly ITraceSource traceSource;

private readonly ConcurrentDictionary<string, (string login, string name, string email, string[] groups)> cache =
new ConcurrentDictionary<string, (string login, string name, string email, string[] groups)>();

/// <summary>
/// Initializes a new instance of the a <see cref="RutaModule"/> object
/// </summary>
Expand Down Expand Up @@ -135,28 +140,55 @@ private void HandleAuthorizeRequestInternal(IRutaHttpContext context)
traceSource.TraceEvent(TraceEventType.Information, 0, "No username in context");
return;
}
var userInformation = this.adInteraction.GetUserInformation(userName);
if (string.IsNullOrWhiteSpace(userInformation.login))

(string loginToSend, string name, string email, string[] groupsToSend) = this.GetUserInformation(userName);
if (loginToSend == null || name == null || email == null)
{
traceSource.TraceEvent(TraceEventType.Information, 0, "No user information in context");
traceSource.TraceEvent(TraceEventType.Information, 0, "No data available");
return;
}

traceSource.TraceEvent(TraceEventType.Information, 0, "Set headers");
if (groupsToSend == null)
{
groupsToSend = new string[0];
}

string loginToSend = ApplyUserSettings(userInformation.login);
string[] groupsToSend = userInformation.groups.Where(group => !string.IsNullOrWhiteSpace(group))
.Select(ApplyGroupSettings)
.ToArray();
traceSource.TraceEvent(TraceEventType.Information, 0, "Set headers");

context.RemoveRequestHeader("Authorization"); // Remove the authorzation header since we are in charge of authentication
context.AddRequestHeader(this.settings.LoginHeader, loginToSend);
context.AddRequestHeader(this.settings.NameHeader, userInformation.name);
context.AddRequestHeader(this.settings.EmailHeader, userInformation.email);
context.AddRequestHeader(this.settings.NameHeader, name);
context.AddRequestHeader(this.settings.EmailHeader, email);
context.AddRequestHeader(this.settings.GroupsHeader, string.Join(",", groupsToSend));
}

private (string login, string name, string email, string[] groups) GetUserInformation(string userName)
{
if (this.cache.TryGetValue(userName, out var cachedValues))
{
traceSource.TraceEvent(TraceEventType.Information, 0, "Returning cached data.");
return cachedValues;
}

var userInformation = this.adInteraction.GetUserInformation(userName);
if (string.IsNullOrWhiteSpace(userInformation.login))
{
traceSource.TraceEvent(TraceEventType.Information, 0, "No user information in context");
return (null, null, null, null);
}

traceSource.TraceEvent(TraceEventType.Information, 0, "Bulding header results and caching");

string loginToSend = ApplyUserSettings(userInformation.login);
string[] groupsToSend = userInformation.groups.Where(group => !string.IsNullOrWhiteSpace(group))
.Select(ApplyGroupSettings)
.ToArray();

this.cache.TryAdd(userName, (loginToSend, userInformation.name, userInformation.email, groupsToSend));

return (loginToSend, userInformation.name, userInformation.email, groupsToSend);
}

/// <summary>
/// Returns a copy of the <paramref name="group"/> object adjusted as necessary based on
/// the settings (DowncaseGroups and AppendString).
Expand All @@ -181,19 +213,11 @@ private void HandleAuthorizeRequestInternal(IRutaHttpContext context)
/// </summary>
/// <param name="source">A <see cref="String"/> on which the action should be performed.</param>
/// <returns>The modified version of <paramref name="source"/>.</returns>
private string AppendIfNeeded(string source)
{
if (string.IsNullOrWhiteSpace(this.settings.AppendString))
{
return source;
}

return $"{source}{this.settings.AppendString}";
}
private string AppendIfNeeded(string source) => string.IsNullOrWhiteSpace(this.settings.AppendString) ? source : $"{source}{this.settings.AppendString}";

/// <summary>
/// Returns a copy of the <paramref name="source"/> object converted to lowercase
/// using the casing rules of the invariant culture if indicated by <paramref name="applyDowncase"/>.
/// using the casing rules of the invariant culture if indicated by <paramref name="applyLowercase"/>.
/// In all other cases <paramref name="source"/> will be returned.
/// </summary>
/// <param name="source">A <see cref="String"/> on which the action should be performed.</param>
Expand Down
4 changes: 0 additions & 4 deletions RutaHttpModule/packages.config

This file was deleted.

42 changes: 12 additions & 30 deletions RutaHttpModuleTest/RutaHttpModuleTest.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
Expand Down Expand Up @@ -39,29 +38,12 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.1.0.8-rc2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.1.0.8-rc2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.5.29.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.5.29\lib\net45\Moq.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Numerics" />
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
Expand All @@ -73,25 +55,25 @@
<Compile Include="SonarAuthPassthroughModuleTest.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<WCFMetadata Include="Service Capabilities\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq">
<Version>4.7.8</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.1.14</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.1.14</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RutaHttpModule\RutaHttpModule.csproj">
<Project>{f582add6-a40a-45f2-a046-abc48a350b16}</Project>
<Name>RutaHttpModule</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service Capabilities\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.1.10-rc2\build\net45\MSTest.TestAdapter.targets')" />
</Project>
8 changes: 0 additions & 8 deletions RutaHttpModuleTest/packages.config

This file was deleted.

0 comments on commit 81921b0

Please sign in to comment.