Skip to content

Commit 9bb8202

Browse files
committed
Figures out a way to add custom username password validator
1 parent 76f595b commit 9bb8202

11 files changed

+235
-53
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using Solid.AspNetCore.Extensions.Wcf.Tests.Abstractions;
2+
using Solid.AspNetCore.Extensions.Wcf.Tests.Host;
3+
using Solid.Testing;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.ServiceModel;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
11+
namespace Solid.AspNetCore.Extensions.Wcf.Tests
12+
{
13+
public class BehaviorTestFixture : IDisposable
14+
{
15+
private IInstanceTestService _singleton;
16+
17+
public BehaviorTestFixture()
18+
{
19+
TestingServer = new TestingServerBuilder()
20+
.AddAspNetCoreHostFactory()
21+
.AddStartup<BehaviorTestStartup>()
22+
.Build();
23+
}
24+
25+
public TestingServer TestingServer { get; }
26+
27+
public IInstanceTestService GetService(string username, string password)
28+
{
29+
if (_singleton == null)
30+
{
31+
var url = new Uri(TestingServer.BaseAddress, "singleton");
32+
var binding = new WS2007HttpBinding(SecurityMode.Message);
33+
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
34+
binding.Security.Message.EstablishSecurityContext = false;
35+
var endpoint = new EndpointAddress(url);
36+
var factory = new ChannelFactory<IInstanceTestService>(binding, endpoint);
37+
38+
factory.Credentials.UserName.UserName = username;
39+
factory.Credentials.UserName.Password = password;
40+
41+
var client = factory.CreateChannel();
42+
_singleton = client;
43+
44+
var channel = client as ICommunicationObject;
45+
channel.Closed += (sender, args) =>
46+
{
47+
_singleton = null;
48+
};
49+
}
50+
return _singleton;
51+
}
52+
53+
public void VerifyUserNamePassword(string username, string password)
54+
{
55+
BehaviorTestStartup.MockUserNamePasswordValidator.Verify(v => v.Validate(username, password));
56+
}
57+
58+
public void Dispose()
59+
{
60+
Close(_singleton);
61+
TestingServer.Dispose();
62+
}
63+
64+
private void Close(object service)
65+
{
66+
var channel = service as ICommunicationObject;
67+
if (channel != null)
68+
channel.Close();
69+
}
70+
}
71+
}

Solid.AspNetCore.Extensions.Wcf.Tests/BehaviorTests.cs

+17-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,26 @@
33
using System.Linq;
44
using System.Text;
55
using System.Threading.Tasks;
6+
using Xunit;
67

78
namespace Solid.AspNetCore.Extensions.Wcf.Tests
89
{
9-
class BehaviorTests
10+
public class BehaviorTests : IClassFixture<BehaviorTestFixture>
1011
{
12+
private BehaviorTestFixture _fixture;
13+
14+
public BehaviorTests(BehaviorTestFixture fixture)
15+
{
16+
_fixture = fixture;
17+
}
18+
19+
[Fact]
20+
public async Task ShouldAddCustomUserNamePasswordValidator()
21+
{
22+
var service = _fixture.GetService("username", "password");
23+
service.InstanceId();
24+
25+
_fixture.VerifyUserNamePassword("username", "password");
26+
}
1127
}
1228
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Moq;
4+
using Solid.AspNetCore.Extensions.Wcf.Tests.Abstractions;
5+
using Solid.AspNetCore.Extensions.Wcf.Tests.Host.Behaviors;
6+
using Solid.AspNetCore.Extensions.Wcf.Tests.Host.Services;
7+
using System;
8+
using System.Collections.Generic;
9+
using System.IdentityModel.Selectors;
10+
using System.Linq;
11+
using System.ServiceModel;
12+
using System.ServiceModel.Description;
13+
using System.Text;
14+
using System.Threading.Tasks;
15+
16+
namespace Solid.AspNetCore.Extensions.Wcf.Tests.Host
17+
{
18+
public class BehaviorTestStartup
19+
{
20+
public static Mock<UserNamePasswordValidator> MockUserNamePasswordValidator;
21+
22+
static BehaviorTestStartup()
23+
{
24+
MockUserNamePasswordValidator = new Mock<UserNamePasswordValidator>();
25+
}
26+
27+
public void ConfigureServices(IServiceCollection services)
28+
{
29+
var binding = new WS2007HttpBinding(SecurityMode.Message);
30+
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
31+
binding.Security.Message.EstablishSecurityContext = false;
32+
33+
services.AddSingleton<UserNamePasswordValidator>(MockUserNamePasswordValidator.Object);
34+
services.AddSingleton<IServiceBehavior, UserNamePasswordValidatorBehavior>();
35+
services.AddWcfServiceWithMetadata<SingletonService>().AddDefaultBinding(binding);
36+
}
37+
38+
public void Configure(IApplicationBuilder builder)
39+
{
40+
builder.UseWcfService<SingletonService, IInstanceTestService>("/singleton");
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
4+
using System.IdentityModel.Selectors;
5+
using System.Linq;
6+
using System.Security.Cryptography.X509Certificates;
7+
using System.ServiceModel;
8+
using System.ServiceModel.Channels;
9+
using System.ServiceModel.Description;
10+
using System.ServiceModel.Security;
11+
using System.Text;
12+
using System.Threading.Tasks;
13+
14+
namespace Solid.AspNetCore.Extensions.Wcf.Tests.Host.Behaviors
15+
{
16+
public class UserNamePasswordValidatorBehavior : ServiceCredentials
17+
{
18+
private UserNamePasswordValidator _validator;
19+
20+
public UserNamePasswordValidatorBehavior(UserNamePasswordValidator validator)
21+
: base()
22+
{
23+
_validator = validator;
24+
25+
UserNameAuthentication.CustomUserNamePasswordValidator = _validator;
26+
UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
27+
ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "localhost");
28+
}
29+
30+
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
31+
{
32+
}
33+
34+
protected override ServiceCredentials CloneCore()
35+
{
36+
return new UserNamePasswordValidatorBehavior(_validator);
37+
}
38+
}
39+
}

Solid.AspNetCore.Extensions.Wcf.Tests/Host/InstanaceTestStartup.cs

-4
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,12 @@ public void ConfigureServices(IServiceCollection services)
1717
{
1818
services.AddWcfServiceWithMetadata<PerCallService>();
1919
services.AddWcfServiceWithMetadata<SingletonService>();
20-
services.AddWcfServiceWithMetadata<ProxiedService>();
21-
//services.AddWcfServiceWithMetadata<DirectService>();
2220
}
2321

2422
public void Configure(IApplicationBuilder builder)
2523
{
2624
builder.UseWcfService<PerCallService, IInstanceTestService>("/percall");
2725
builder.UseWcfService<SingletonService, IInstanceTestService>("/singleton");
28-
builder.UseWcfService<ProxiedService, IProxiedService>("/proxied");
29-
//builder.UseWcfService<DirectService, IDirectService>("/direct", true);
3026
}
3127
}
3228
}

Solid.AspNetCore.Extensions.Wcf.Tests/Solid.AspNetCore.Extensions.Wcf.Tests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<ItemGroup>
1010
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.1" />
1111
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
12+
<PackageReference Include="Moq" Version="4.8.2" />
1213
<PackageReference Include="Solid.Testing.AspNetCore" Version="1.0.16" />
1314
<PackageReference Include="xunit" Version="2.3.1" />
1415
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />

Solid.AspNetCore.Extensions.Wcf/Factories/HostInitializerFactory.cs

-13
This file was deleted.

Solid.AspNetCore.Extensions.Wcf/Factories/ServiceHostFactory.cs

+17-23
Original file line numberDiff line numberDiff line change
@@ -31,41 +31,35 @@ public AspNetCoreServiceHost<TService> Create<TService>()
3131
else
3232
host = new AspNetCoreServiceHost<TService>();
3333

34-
AddGlobalBehaviors(host);
35-
AddInstanceBehaviors(host);
36-
//EnsureSingleInstanceContextMode(host);
34+
host.Initializing += AddGlobalBehaviors;
35+
host.Initializing += (sender, args) => AddInstanceBehaviors<TService>(sender, args);
3736

3837
return host;
3938
}
4039

41-
private void AddGlobalBehaviors<TService>(AspNetCoreServiceHost<TService> host)
40+
private void AddGlobalBehaviors(object sender, EventArgs args)
4241
{
43-
host.DescriptionInitialized += (sender, args) =>
44-
{
45-
var behaviors = _provider.GetServices<IServiceBehavior>();
46-
foreach (var behavior in behaviors)
47-
host.Description.Behaviors.Add(behavior);
48-
};
42+
var host = sender as ServiceHost;
43+
var behaviors = _provider.GetServices<IServiceBehavior>().ToArray();
44+
AddBehaviorsToHost(host, behaviors);
4945
}
5046

51-
private void AddInstanceBehaviors<TService>(AspNetCoreServiceHost<TService> host)
47+
private void AddInstanceBehaviors<TService>(object sender, EventArgs args)
5248
{
53-
host.DescriptionInitialized += (sender, args) =>
54-
{
55-
var behaviorProviders = _provider.GetServices<ServiceBehaviorProvider<TService>>();
56-
foreach (var provider in behaviorProviders)
57-
host.Description.Behaviors.Add(provider.Behavior);
58-
};
49+
var host = sender as ServiceHost;
50+
var behaviors = _provider.GetServices<ServiceBehaviorProvider<TService>>().Select(p => p.Behavior).ToArray();
51+
AddBehaviorsToHost(host, behaviors);
5952
}
6053

61-
private void EnsureSingleInstanceContextMode<TService>(AspNetCoreServiceHost<TService> host)
54+
private void AddBehaviorsToHost(ServiceHost host, IServiceBehavior[] behaviors)
6255
{
63-
host.DescriptionInitialized += (sender, args) =>
56+
foreach (var behavior in behaviors)
6457
{
65-
var behaviors = host.Description.Behaviors.OfType<ServiceBehaviorAttribute>().Where(b => b.InstanceContextMode != InstanceContextMode.Single);
66-
foreach (var behavior in behaviors)
67-
behavior.InstanceContextMode = InstanceContextMode.Single;
68-
};
58+
if (typeof(ServiceCredentials).IsAssignableFrom(behavior.GetType()))
59+
host.Description.Behaviors.Remove(typeof(ServiceCredentials));
60+
61+
host.Description.Behaviors.Add(behavior);
62+
}
6963
}
7064
}
7165
}

Solid.AspNetCore.Extensions.Wcf/ServiceCollectionExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ private static IServiceCollection TryAddServiceModel(this IServiceCollection ser
128128
services.TryAddSingleton<IBaseAddressFactory, BaseAddressFactory>();
129129
services.TryAddTransient<IServiceBehavior, AspNetCoreInstanceProviderBehavior>(comparer);
130130
services.TryAddTransient<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>(comparer);
131+
services.TryAddTransient<IServiceBehavior, MatchAnyAddressServiceBehavior>(comparer);
131132
services.TryAddSingleton(typeof(EndpointBuilder<>));
132133

133134
return services;

Solid.AspNetCore.Extensions.Wcf/ServiceModel/AspNetCoreServiceHost.cs

+11-12
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,17 @@ public AspNetCoreServiceHost()
2525
{
2626
}
2727

28-
public event EventHandler DescriptionInitializing;
29-
public event EventHandler DescriptionInitialized;
28+
public event EventHandler Initializing;
3029

3130
public override void Initialize(IEnumerable<Uri> baseAddresses)
3231
{
33-
if (DescriptionInitializing != null)
34-
DescriptionInitializing(this, EventArgs.Empty);
35-
3632
foreach (var baseAddress in baseAddresses)
3733
{
3834
if (_instance == null)
3935
InitializeDescription(typeof(TService), new UriSchemeKeyedCollection(baseAddress));
4036
else
4137
InitializeDescription(_instance, new UriSchemeKeyedCollection(baseAddress));
4238
}
43-
44-
if (DescriptionInitialized != null)
45-
DescriptionInitialized(this, EventArgs.Empty);
46-
47-
foreach (var endpoint in _endpoints)
48-
AddServiceEndpoint(endpoint.Contract, endpoint.Binding, endpoint.Path);
4939
}
5040

5141
public void AddEndpoint<TContract>(Binding binding, string path)
@@ -56,7 +46,16 @@ public void AddEndpoint<TContract>(Binding binding, string path)
5646
Binding = binding,
5747
Path = path
5848
});
59-
}
49+
}
50+
51+
protected override void InitializeRuntime()
52+
{
53+
if (Initializing != null)
54+
Initializing(this, EventArgs.Empty);
55+
foreach (var endpoint in _endpoints)
56+
AddServiceEndpoint(endpoint.Contract, endpoint.Binding, endpoint.Path);
57+
base.InitializeRuntime();
58+
}
6059
}
6160

6261
internal abstract class AspNetCoreServiceHost : ServiceHost
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
4+
using System.Linq;
5+
using System.ServiceModel;
6+
using System.ServiceModel.Channels;
7+
using System.ServiceModel.Description;
8+
using System.ServiceModel.Dispatcher;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace Solid.AspNetCore.Extensions.Wcf.ServiceModel.Description
13+
{
14+
internal class MatchAnyAddressServiceBehavior : IServiceBehavior
15+
{
16+
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
17+
{
18+
}
19+
20+
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
21+
{
22+
foreach (var dispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>())
23+
{
24+
foreach (var endpoint in dispatcher.Endpoints)
25+
{
26+
endpoint.AddressFilter = new MatchAllMessageFilter();
27+
}
28+
}
29+
}
30+
31+
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
32+
{
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)