Skip to content

Commit 81921b0

Browse files
authored
Merge pull request #15 from jabbera/cacheAdResults
Cache AD results for performance
2 parents e9f468d + f368629 commit 81921b0

6 files changed

Lines changed: 70 additions & 81 deletions

File tree

RutaHttpModule/AdInteraction.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,21 @@ internal AdInteraction()
2424

2525
string usernameOnly = domainUsername.RemoveDomain();
2626

27-
string login = null, name = null, email = null;
28-
string[] groups = null;
29-
3027
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
3128
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, usernameOnly))
3229
{
3330
if (user?.DistinguishedName.EndsWith(this.settings.AdUserBaseDn, StringComparison.OrdinalIgnoreCase) != true)
3431
{
35-
return (login, name, email, groups);
32+
return (null, null, null, null);
3633
}
3734

38-
login = usernameOnly;
39-
name = user.Name;
40-
email = user.EmailAddress;
41-
groups = user.GetGroupsFast(this.settings.AdUserBaseDn, this.settings.AdGroupBaseDn).ToArray();
42-
}
35+
string login = usernameOnly;
36+
string name = user.Name;
37+
string email = user.EmailAddress;
38+
string[] groups = user.GetGroupsFast(this.settings.AdUserBaseDn, this.settings.AdGroupBaseDn).ToArray();
4339

44-
return (login, name, email, groups);
40+
return (login, name, email, groups);
41+
}
4542
}
4643
}
4744
}

RutaHttpModule/RutaHttpModule.csproj

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@
3939
<Reference Include="System.Core" />
4040
<Reference Include="System.DirectoryServices" />
4141
<Reference Include="System.DirectoryServices.AccountManagement" />
42-
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
43-
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
44-
<Private>True</Private>
45-
</Reference>
4642
<Reference Include="System.Web" />
4743
<Reference Include="System.Xml.Linq" />
4844
<Reference Include="System.Data.DataSetExtensions" />
@@ -74,13 +70,15 @@
7470
</ItemGroup>
7571
<ItemGroup>
7672
<None Include="app.config" />
77-
<None Include="packages.config">
78-
<SubType>Designer</SubType>
79-
</None>
8073
<None Include="Properties\Settings.settings">
8174
<Generator>SettingsSingleFileGenerator</Generator>
8275
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
8376
</None>
8477
</ItemGroup>
78+
<ItemGroup>
79+
<PackageReference Include="System.ValueTuple">
80+
<Version>4.3.0</Version>
81+
</PackageReference>
82+
</ItemGroup>
8583
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
8684
</Project>

RutaHttpModule/RutaModule.cs

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace RutaHttpModule
1+
using System.Collections.Concurrent;
2+
3+
namespace RutaHttpModule
24
{
35
using System;
46
using System.Diagnostics;
@@ -30,6 +32,9 @@ public sealed class RutaModule : IHttpModule
3032
/// </summary>
3133
private readonly ITraceSource traceSource;
3234

35+
private readonly ConcurrentDictionary<string, (string login, string name, string email, string[] groups)> cache =
36+
new ConcurrentDictionary<string, (string login, string name, string email, string[] groups)>();
37+
3338
/// <summary>
3439
/// Initializes a new instance of the a <see cref="RutaModule"/> object
3540
/// </summary>
@@ -135,28 +140,55 @@ private void HandleAuthorizeRequestInternal(IRutaHttpContext context)
135140
traceSource.TraceEvent(TraceEventType.Information, 0, "No username in context");
136141
return;
137142
}
138-
139-
var userInformation = this.adInteraction.GetUserInformation(userName);
140-
if (string.IsNullOrWhiteSpace(userInformation.login))
143+
144+
(string loginToSend, string name, string email, string[] groupsToSend) = this.GetUserInformation(userName);
145+
if (loginToSend == null || name == null || email == null)
141146
{
142-
traceSource.TraceEvent(TraceEventType.Information, 0, "No user information in context");
147+
traceSource.TraceEvent(TraceEventType.Information, 0, "No data available");
143148
return;
144149
}
145150

146-
traceSource.TraceEvent(TraceEventType.Information, 0, "Set headers");
151+
if (groupsToSend == null)
152+
{
153+
groupsToSend = new string[0];
154+
}
147155

148-
string loginToSend = ApplyUserSettings(userInformation.login);
149-
string[] groupsToSend = userInformation.groups.Where(group => !string.IsNullOrWhiteSpace(group))
150-
.Select(ApplyGroupSettings)
151-
.ToArray();
156+
traceSource.TraceEvent(TraceEventType.Information, 0, "Set headers");
152157

153158
context.RemoveRequestHeader("Authorization"); // Remove the authorzation header since we are in charge of authentication
154159
context.AddRequestHeader(this.settings.LoginHeader, loginToSend);
155-
context.AddRequestHeader(this.settings.NameHeader, userInformation.name);
156-
context.AddRequestHeader(this.settings.EmailHeader, userInformation.email);
160+
context.AddRequestHeader(this.settings.NameHeader, name);
161+
context.AddRequestHeader(this.settings.EmailHeader, email);
157162
context.AddRequestHeader(this.settings.GroupsHeader, string.Join(",", groupsToSend));
158163
}
159164

165+
private (string login, string name, string email, string[] groups) GetUserInformation(string userName)
166+
{
167+
if (this.cache.TryGetValue(userName, out var cachedValues))
168+
{
169+
traceSource.TraceEvent(TraceEventType.Information, 0, "Returning cached data.");
170+
return cachedValues;
171+
}
172+
173+
var userInformation = this.adInteraction.GetUserInformation(userName);
174+
if (string.IsNullOrWhiteSpace(userInformation.login))
175+
{
176+
traceSource.TraceEvent(TraceEventType.Information, 0, "No user information in context");
177+
return (null, null, null, null);
178+
}
179+
180+
traceSource.TraceEvent(TraceEventType.Information, 0, "Bulding header results and caching");
181+
182+
string loginToSend = ApplyUserSettings(userInformation.login);
183+
string[] groupsToSend = userInformation.groups.Where(group => !string.IsNullOrWhiteSpace(group))
184+
.Select(ApplyGroupSettings)
185+
.ToArray();
186+
187+
this.cache.TryAdd(userName, (loginToSend, userInformation.name, userInformation.email, groupsToSend));
188+
189+
return (loginToSend, userInformation.name, userInformation.email, groupsToSend);
190+
}
191+
160192
/// <summary>
161193
/// Returns a copy of the <paramref name="group"/> object adjusted as necessary based on
162194
/// the settings (DowncaseGroups and AppendString).
@@ -181,19 +213,11 @@ private void HandleAuthorizeRequestInternal(IRutaHttpContext context)
181213
/// </summary>
182214
/// <param name="source">A <see cref="String"/> on which the action should be performed.</param>
183215
/// <returns>The modified version of <paramref name="source"/>.</returns>
184-
private string AppendIfNeeded(string source)
185-
{
186-
if (string.IsNullOrWhiteSpace(this.settings.AppendString))
187-
{
188-
return source;
189-
}
190-
191-
return $"{source}{this.settings.AppendString}";
192-
}
216+
private string AppendIfNeeded(string source) => string.IsNullOrWhiteSpace(this.settings.AppendString) ? source : $"{source}{this.settings.AppendString}";
193217

194218
/// <summary>
195219
/// Returns a copy of the <paramref name="source"/> object converted to lowercase
196-
/// using the casing rules of the invariant culture if indicated by <paramref name="applyDowncase"/>.
220+
/// using the casing rules of the invariant culture if indicated by <paramref name="applyLowercase"/>.
197221
/// In all other cases <paramref name="source"/> will be returned.
198222
/// </summary>
199223
/// <param name="source">A <see cref="String"/> on which the action should be performed.</param>

RutaHttpModule/packages.config

Lines changed: 0 additions & 4 deletions
This file was deleted.

RutaHttpModuleTest/RutaHttpModuleTest.csproj

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<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')" />
43
<PropertyGroup>
54
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
65
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -39,29 +38,12 @@
3938
<WarningLevel>4</WarningLevel>
4039
</PropertyGroup>
4140
<ItemGroup>
42-
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
43-
<HintPath>..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
44-
<Private>True</Private>
45-
</Reference>
46-
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
47-
<HintPath>..\packages\MSTest.TestFramework.1.0.8-rc2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
48-
</Reference>
49-
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
50-
<HintPath>..\packages\MSTest.TestFramework.1.0.8-rc2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
51-
</Reference>
52-
<Reference Include="Moq, Version=4.5.29.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
53-
<HintPath>..\packages\Moq.4.5.29\lib\net45\Moq.dll</HintPath>
54-
</Reference>
5541
<Reference Include="System" />
5642
<Reference Include="System.ComponentModel.Composition" />
5743
<Reference Include="System.Core" />
5844
<Reference Include="System.DirectoryServices.AccountManagement" />
5945
<Reference Include="System.IO.Compression.FileSystem" />
6046
<Reference Include="System.Numerics" />
61-
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
62-
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
63-
<Private>True</Private>
64-
</Reference>
6547
<Reference Include="System.Web" />
6648
<Reference Include="System.Xml" />
6749
<Reference Include="System.Xml.Linq" />
@@ -73,25 +55,25 @@
7355
<Compile Include="SonarAuthPassthroughModuleTest.cs" />
7456
</ItemGroup>
7557
<ItemGroup>
76-
<None Include="packages.config" />
58+
<WCFMetadata Include="Service Capabilities\" />
59+
</ItemGroup>
60+
<ItemGroup>
61+
<PackageReference Include="Moq">
62+
<Version>4.7.8</Version>
63+
</PackageReference>
64+
<PackageReference Include="MSTest.TestAdapter">
65+
<Version>1.1.14</Version>
66+
</PackageReference>
67+
<PackageReference Include="MSTest.TestFramework">
68+
<Version>1.1.14</Version>
69+
</PackageReference>
7770
</ItemGroup>
7871
<ItemGroup>
7972
<ProjectReference Include="..\RutaHttpModule\RutaHttpModule.csproj">
8073
<Project>{f582add6-a40a-45f2-a046-abc48a350b16}</Project>
8174
<Name>RutaHttpModule</Name>
8275
</ProjectReference>
8376
</ItemGroup>
84-
<ItemGroup>
85-
<WCFMetadata Include="Service Capabilities\" />
86-
</ItemGroup>
8777
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
8878
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
89-
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
90-
<PropertyGroup>
91-
<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>
92-
</PropertyGroup>
93-
<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'))" />
94-
<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'))" />
95-
</Target>
96-
<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')" />
9779
</Project>

RutaHttpModuleTest/packages.config

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)