Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

work on providing a generic authentication process for OpenRasta

Basic authentication is supported
Removed the old Digest authentication but code still there as having issues with building subprojects
  • Loading branch information...
commit 25ee8bfbf610cea17626a9e7dfede565f662d7bb 1 parent 418d388
Scott Littlewood authored

Showing 39 changed files with 929 additions and 70 deletions. Show diff stats Hide diff stats

  1. +0 3  src/core/OpenRasta.Tests.Integration/Binding/CustomSurrogates.cs
  2. +3 1 src/core/OpenRasta.Tests.Integration/OpenRasta.Tests.Integration.csproj
  3. +111 0 src/core/OpenRasta.Tests.Unit/Authentication/Basic/BasicAuthenticationScheme_Specification.cs
  4. +16 14 src/core/OpenRasta.Tests.Unit/OpenRasta.Tests.Unit.csproj
  5. +109 0 src/core/OpenRasta.Tests.Unit/Pipeline/Contributors/AuthenticationChallenger_Specification.cs
  6. +160 0 src/core/OpenRasta.Tests.Unit/Pipeline/Contributors/Authentication_Specification.cs
  7. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/HandlerMethodFiltersInvoker_Specification.cs
  8. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/HandlerMethodInvoker_Specification.cs
  9. 0  ...penRasta.Tests.Unit/{Web → }/Pipeline/Contributors/HandlerMethodrequestEntityResolver_Specification.cs
  10. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/HandlerResolver_Specification.cs
  11. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/HttpMethodOverrider_Specification.cs
  12. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/InvalidRequestEntityRemover_Specification.cs
  13. 0  ...core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/OperationCreationContributor_Specification.cs
  14. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/OperationProcessors_Specification.cs
  15. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/RequestEntityReader_Specification.cs
  16. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/ResourceTypeResolver_Specification.cs
  17. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/ResponseEntityCodecResolver_Specification.cs
  18. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/ResponseEntityWriter_Specification.cs
  19. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/Contributors/UriDecoratorsController_Specification.cs
  20. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/DigestCredentialsReader_Specification.cs
  21. 0  src/core/OpenRasta.Tests.Unit/{Web → }/Pipeline/PipelineRunner_Specification.cs
  22. +6 24 src/core/OpenRasta.Tests.Unit/openrasta_context.cs
  23. +26 0 src/core/OpenRasta/Authentication/AuthenticationResult.cs
  24. +18 0 src/core/OpenRasta/Authentication/Basic/BasicAuthRequestHeader.cs
  25. +55 0 src/core/OpenRasta/Authentication/Basic/BasicAuthenticationScheme.cs
  26. +8 0 src/core/OpenRasta/Authentication/Basic/IBasicAuthenticator.cs
  27. +156 0 src/core/OpenRasta/Authentication/Digest/DigestAuthRequestHeader.cs
  28. +34 0 src/core/OpenRasta/Authentication/Digest/DigestAuthenticationScheme.cs
  29. +8 0 src/core/OpenRasta/Authentication/Digest/IDigestAuthenticator.cs
  30. +13 0 src/core/OpenRasta/Authentication/IAuthenticationScheme.cs
  31. +2 1  src/core/OpenRasta/Configuration/DefaultDependencyRegistrar.cs
  32. +22 5 src/core/OpenRasta/OpenRasta.csproj
  33. +41 0 src/core/OpenRasta/Pipeline/Contributors/AuthenticationChallengerContributor.cs
  34. +91 0 src/core/OpenRasta/Pipeline/Contributors/AuthenticationContributor.cs
  35. +5 0 src/core/OpenRasta/Pipeline/KnownStages.cs
  36. +0 21 src/core/OpenRasta/Security/RequiresAuthenticationAttribute.cs
  37. +27 0 src/core/OpenRasta/Security/RequiresAuthenticationInterceptor.cs
  38. +17 0 src/core/OpenRasta/StringExtensions.cs
  39. +1 1  src/openbastard/OpenBastard/OpenBastard.csproj
3  src/core/OpenRasta.Tests.Integration/Binding/CustomSurrogates.cs
... ... @@ -1,7 +1,6 @@
1 1 using System;
2 2 using System.Net;
3 3 using System.Text;
4   -using DigestAuthentication_Specification;
5 4 using NUnit.Framework;
6 5 using OpenRasta.Configuration;
7 6 using OpenRasta.Configuration.Fluent;
@@ -47,8 +46,6 @@ public surrogates_context()
47 46 {
48 47 ConfigureServer(() =>
49 48 {
50   - DependencyManager.GetService<IDependencyResolver>()
51   - .AddDependency<IAuthenticationProvider, FakeAuthProvider>();
52 49
53 50 ResourceSpace.Has.ResourcesOfType<Customer>()
54 51 .AtUri("/customer/{id}")
4 src/core/OpenRasta.Tests.Integration/OpenRasta.Tests.Integration.csproj
@@ -73,7 +73,6 @@
73 73 <Compile Include="Regressions\78.cs" />
74 74 <Compile Include="Regressions\96.cs" />
75 75 <Compile Include="Regressions\135.cs" />
76   - <Compile Include="Security\DigestAuthentication_Specification.cs" />
77 76 <Compile Include="server_context.cs" />
78 77 <Compile Include="Stubs\Customer.cs" />
79 78 <Compile Include="Stubs\CustomerHandler.cs" />
@@ -99,6 +98,9 @@
99 98 <Name>OpenRasta.Testing</Name>
100 99 </ProjectReference>
101 100 </ItemGroup>
  101 + <ItemGroup>
  102 + <Folder Include="Security\" />
  103 + </ItemGroup>
102 104 <Import Project="..\..\..\build\defaults.targets" />
103 105 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
104 106 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
111 src/core/OpenRasta.Tests.Unit/Authentication/Basic/BasicAuthenticationScheme_Specification.cs
... ... @@ -0,0 +1,111 @@
  1 +using Moq;
  2 +using NUnit.Framework;
  3 +using OpenRasta.Authentication;
  4 +using OpenRasta.Authentication.Basic;
  5 +using OpenRasta.Hosting.InMemory;
  6 +using OpenRasta.Testing;
  7 +
  8 +namespace BasicAuthenticationScheme_Specification
  9 +{
  10 + [TestFixture]
  11 + public class BasicAuthenticationScheme_Specification
  12 + {
  13 + Mock<IBasicAuthenticator> _mockAuthenticator;
  14 + InMemoryRequest _request;
  15 + BasicAuthenticationScheme _basicScheme;
  16 +
  17 + [SetUp]
  18 + public void BeforeEachTest()
  19 + {
  20 + _mockAuthenticator = new Mock<IBasicAuthenticator>();
  21 + _request = new InMemoryRequest();
  22 + _basicScheme = new BasicAuthenticationScheme(_mockAuthenticator.Object);
  23 + }
  24 +
  25 + [TearDown]
  26 + public void AfterEachTest()
  27 + {
  28 + _mockAuthenticator.VerifyAll();
  29 + }
  30 +
  31 + [Test]
  32 + public void Given_AValidBasicAuthHeader_When_TheRequestIsAuthenticated_Then_TheResult_IsSuccess_And_UsernameIsSet_And_RolesAreSet()
  33 + {
  34 + // given
  35 + string validAuthString = "Basic U2F1c2FnZTphbmQgbWFzaA==";
  36 + string username = "Sausage";
  37 + string password = "and mash";
  38 +
  39 + string[] userRoles = new[] { "Admin", "Manager", "Developer" };
  40 +
  41 + _request.Headers["Authorization"] = validAuthString;
  42 +
  43 + _mockAuthenticator
  44 + .Expect(auth => auth.Authenticate(It.Is<BasicAuthRequestHeader>(h => h.Username == username && h.Password == password)))
  45 + .Returns(new AuthenticationResult.Success(username, userRoles));
  46 +
  47 + // when
  48 + var result = _basicScheme.Authenticate(_request);
  49 +
  50 + // then
  51 + result.ShouldBeOfType<AuthenticationResult.Success>();
  52 + var success = result as AuthenticationResult.Success;
  53 +
  54 + success.Username.ShouldBe(username);
  55 + success.Roles.ShouldHaveSameElementsAs(userRoles);
  56 + }
  57 +
  58 + [Test]
  59 + public void Given_AMalformedBasicAuthHeader_When_TheRequestIsAuthenticated_Then_TheResult_IsMalformed()
  60 + {
  61 + // given
  62 + string malformedAuthString = "Basic notAValidBase64String!!!";
  63 + _request.Headers["Authorization"] = malformedAuthString;
  64 +
  65 + // when
  66 + var result = _basicScheme.Authenticate(_request);
  67 +
  68 + // then
  69 + result.ShouldBeOfType<AuthenticationResult.MalformedCredentials>();
  70 + }
  71 +
  72 + [Test]
  73 + public void Given_ABasicAuthenticatorReturnsFailed_When_TheRequestIsAuthenticated_Then_TheResult_IsFailed()
  74 + {
  75 + // given
  76 + string authString = "Basic U2F1c2FnZTphbmQgbWFzaA==";
  77 + string username = "Sausage";
  78 + string password = "and mash";
  79 + _request.Headers["Authorization"] = authString;
  80 +
  81 + _mockAuthenticator
  82 + .Expect(auth => auth.Authenticate(It.Is<BasicAuthRequestHeader>(h => h.Username == username && h.Password == password)))
  83 + .Returns(new AuthenticationResult.Failed());
  84 +
  85 + // when
  86 + var result = _basicScheme.Authenticate(_request);
  87 +
  88 + // then
  89 + result.ShouldBeOfType<AuthenticationResult.Failed>();
  90 + }
  91 +
  92 + [Test]
  93 + public void Given_ABasicAuthenticatorWithARealm_When_ChallengingAResponse_Then_TheResponseHasAWWWAuthenticateHeader()
  94 + {
  95 + // given
  96 + string realm = "Lex Luthors Palace";
  97 + var response = new InMemoryResponse();
  98 +
  99 + _mockAuthenticator
  100 + .ExpectGet(auth => auth.Realm)
  101 + .Returns(realm);
  102 +
  103 + // when
  104 + _basicScheme.Challenge(response);
  105 +
  106 + // then
  107 + var expectedChallengeHeader = string.Format("Basic realm=\"{0}\"", realm);
  108 + response.Headers.ShouldContain("WWW-Authenticate", expectedChallengeHeader);
  109 + }
  110 + }
  111 +}
30 src/core/OpenRasta.Tests.Unit/OpenRasta.Tests.Unit.csproj
@@ -61,6 +61,7 @@
61 61 <Compile Include="..\..\CommonInfo.cs">
62 62 <Link>Properties\CommonInfo.cs</Link>
63 63 </Compile>
  64 + <Compile Include="Authentication\Basic\BasicAuthenticationScheme_Specification.cs" />
64 65 <Compile Include="Binding\DefaultBinderLocator_Specification.cs" />
65 66 <Compile Include="Binding\KeyedValuesBinder_Specification.cs" />
66 67 <Compile Include="Codecs\ApplicationOctetStreamCodec_Specification.cs" />
@@ -109,6 +110,8 @@
109 110 <Compile Include="OperationModel\MethodBased\MethodBasedOperation_Specification.cs" />
110 111 <Compile Include="OperationModel\MethodBased\TypeExclusionFilter_Specification.cs" />
111 112 <Compile Include="OperationModel\OperationHydration_Spec.cs" />
  113 + <Compile Include="Pipeline\Contributors\AuthenticationChallenger_Specification.cs" />
  114 + <Compile Include="Pipeline\Contributors\Authentication_Specification.cs" />
112 115 <Compile Include="Security\RequiresAuthenticationInterceptor_Specification.cs" />
113 116 <Compile Include="Security\RequiresRoleInterceptor_Specification.cs" />
114 117 <Compile Include="TypeSystem\Members_Specification.cs" />
@@ -149,20 +152,19 @@
149 152 <Compile Include="Web\Markup\TextArea_Specification.cs" />
150 153 <Compile Include="Web\Markup\TextNode_Specification.cs" />
151 154 <Compile Include="Web\Markup\XhtmlDOM_Specification.cs" />
152   - <Compile Include="Web\Pipeline\Contributors\HandlerMethodFiltersInvoker_Specification.cs" />
153   - <Compile Include="Web\Pipeline\Contributors\OperationCreationContributor_Specification.cs" />
154   - <Compile Include="Web\Pipeline\Contributors\OperationProcessors_Specification.cs" />
155   - <Compile Include="Web\Pipeline\Contributors\ResponseEntityWriter_Specification.cs" />
156   - <Compile Include="Web\Pipeline\Contributors\ResponseEntityCodecResolver_Specification.cs" />
157   - <Compile Include="Web\Pipeline\Contributors\HandlerMethodInvoker_Specification.cs" />
158   - <Compile Include="Web\Pipeline\Contributors\RequestEntityReader_Specification.cs" />
159   - <Compile Include="Web\Pipeline\Contributors\InvalidRequestEntityRemover_Specification.cs" />
160   - <Compile Include="Web\Pipeline\Contributors\HandlerResolver_Specification.cs" />
161   - <Compile Include="Web\Pipeline\Contributors\HttpMethodOverrider_Specification.cs" />
162   - <Compile Include="Web\Pipeline\Contributors\ResourceTypeResolver_Specification.cs" />
163   - <Compile Include="Web\Pipeline\Contributors\UriDecoratorsController_Specification.cs" />
164   - <Compile Include="Web\Pipeline\DigestCredentialsReader_Specification.cs" />
165   - <Compile Include="Web\Pipeline\PipelineRunner_Specification.cs" />
  155 + <Compile Include="Pipeline\Contributors\HandlerMethodFiltersInvoker_Specification.cs" />
  156 + <Compile Include="Pipeline\Contributors\OperationCreationContributor_Specification.cs" />
  157 + <Compile Include="Pipeline\Contributors\OperationProcessors_Specification.cs" />
  158 + <Compile Include="Pipeline\Contributors\ResponseEntityWriter_Specification.cs" />
  159 + <Compile Include="Pipeline\Contributors\ResponseEntityCodecResolver_Specification.cs" />
  160 + <Compile Include="Pipeline\Contributors\HandlerMethodInvoker_Specification.cs" />
  161 + <Compile Include="Pipeline\Contributors\RequestEntityReader_Specification.cs" />
  162 + <Compile Include="Pipeline\Contributors\InvalidRequestEntityRemover_Specification.cs" />
  163 + <Compile Include="Pipeline\Contributors\HandlerResolver_Specification.cs" />
  164 + <Compile Include="Pipeline\Contributors\HttpMethodOverrider_Specification.cs" />
  165 + <Compile Include="Pipeline\Contributors\ResourceTypeResolver_Specification.cs" />
  166 + <Compile Include="Pipeline\Contributors\UriDecoratorsController_Specification.cs" />
  167 + <Compile Include="Pipeline\PipelineRunner_Specification.cs" />
166 168 <Compile Include="UriTemplate_Specification.cs" />
167 169 <Compile Include="Web\Reflection_Specification.cs" />
168 170 <Compile Include="Web\TemplatedUriResolver_Specification.cs" />
109 src/core/OpenRasta.Tests.Unit/Pipeline/Contributors/AuthenticationChallenger_Specification.cs
... ... @@ -0,0 +1,109 @@
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using Moq;
  6 +using NUnit.Framework;
  7 +using OpenRasta.Authentication;
  8 +using OpenRasta.DI;
  9 +using OpenRasta.Pipeline;
  10 +using OpenRasta.Pipeline.Contributors;
  11 +using OpenRasta.Testing;
  12 +using OpenRasta.Tests;
  13 +using OpenRasta.Web;
  14 +
  15 +namespace given_an_authentication_contributor
  16 +{
  17 + public abstract class given_an_authentication_contributor : openrasta_context
  18 + {
  19 + protected override void SetUp()
  20 + {
  21 + base.SetUp();
  22 + given_pipeline_contributor<AuthenticationChallengerContributor>();
  23 + }
  24 + }
  25 +
  26 + public class when_the_pipeline_is_notified_IOperationExecution : given_an_authentication_contributor
  27 + {
  28 + [Test]
  29 + public void then_authentication_challenger_is_invoked()
  30 + {
  31 + // given
  32 +
  33 + // when
  34 + when_sending_notification<KnownStages.IOperationExecution>();
  35 +
  36 + // then
  37 + IsContributorExecuted.ShouldBeTrue();
  38 + }
  39 + }
  40 +
  41 + public class when_the_pipeline_is_notified_IResponseCoding : given_an_authentication_contributor
  42 + {
  43 + [Test]
  44 + public void then_authentication_challenger_is_invoked()
  45 + {
  46 + // given
  47 +
  48 + // when
  49 + when_sending_notification<KnownStages.IResponseCoding>();
  50 +
  51 + // then
  52 + IsContributorExecuted.ShouldBeTrue();
  53 + }
  54 + }
  55 +
  56 + namespace _and_scheme
  57 + {
  58 +
  59 + public abstract class _and_scheme : given_an_authentication_contributor
  60 + {
  61 + protected Mock<IAuthenticationScheme> mockScheme = new Mock<IAuthenticationScheme>();
  62 +
  63 + protected override void SetUp()
  64 + {
  65 + base.SetUp();
  66 + given_dependency(mockScheme.Object);
  67 + given_pipeline_contributor<AuthenticationChallengerContributor>();
  68 + }
  69 + }
  70 +
  71 + public class when_the_context_is_unauthorized : _and_scheme
  72 + {
  73 + [Test]
  74 + public void then_the_authentication_scheme_is_challenged()
  75 + {
  76 + // given
  77 + Context.OperationResult = new OperationResult.Unauthorized();
  78 +
  79 + // when
  80 + when_sending_notification<KnownStages.IOperationExecution>();
  81 +
  82 + // then
  83 + mockScheme.Verify(s => s.Challenge(Context.Response));
  84 + }
  85 + }
  86 +
  87 + public class when_the_context_is_ok : given_an_authentication_contributor
  88 + {
  89 + [Test]
  90 + public void then_the_authentication_scheme_is_not_challenged()
  91 + {
  92 + // given
  93 + var mockScheme = new Mock<IAuthenticationScheme>(MockBehavior.Strict);
  94 +
  95 + given_dependency(mockScheme.Object);
  96 +
  97 + Context.OperationResult = new OperationResult.OK();
  98 +
  99 + // when
  100 + when_sending_notification<KnownStages.IOperationExecution>();
  101 +
  102 + // then
  103 + mockScheme.VerifyAll();
  104 + }
  105 + }
  106 + }
  107 +}
  108 +
  109 +
160 src/core/OpenRasta.Tests.Unit/Pipeline/Contributors/Authentication_Specification.cs
... ... @@ -0,0 +1,160 @@
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Linq;
  4 +using System.Text;
  5 +using Moq;
  6 +using NUnit.Framework;
  7 +using OpenRasta.Authentication;
  8 +using OpenRasta.DI;
  9 +using OpenRasta.Pipeline;
  10 +using OpenRasta.Pipeline.Contributors;
  11 +using OpenRasta.Testing;
  12 +using OpenRasta.Tests;
  13 +using OpenRasta.Web;
  14 +
  15 +namespace Authentication_Specification
  16 +{
  17 + [TestFixture]
  18 + public class Authentication_Specification : openrasta_context
  19 + {
  20 + [Test]
  21 + public void Authentication_IsInvokedAfterIBegin()
  22 + {
  23 + // given
  24 + given_pipeline_contributor<AuthenticationContributor>();
  25 +
  26 + // when
  27 + when_sending_notification<KnownStages.IBegin>();
  28 +
  29 + // then
  30 + IsContributorExecuted.ShouldBeTrue();
  31 + }
  32 +
  33 + [Test]
  34 + public void Authentication_IsInvokedBeforeIHandlerSelection()
  35 + {
  36 + // given
  37 + given_pipeline_contributor<AuthenticationContributor>();
  38 +
  39 + // when
  40 + when_sending_notification<KnownStages.IHandlerSelection>();
  41 +
  42 + // then
  43 + IsContributorExecuted.ShouldBeTrue();
  44 + }
  45 +
  46 + [Test]
  47 + public void NoAuthHeader()
  48 + {
  49 + // given
  50 + given_pipeline_contributor<AuthenticationContributor>();
  51 +
  52 + // when
  53 + var result = when_sending_notification<KnownStages.IHandlerSelection>();
  54 +
  55 + // then
  56 + result.ShouldBe(PipelineContinuation.Continue);
  57 + }
  58 +
  59 + [Test]
  60 + public void AuthHeaderWithUnsupportedScheme()
  61 + {
  62 + // given
  63 + given_pipeline_contributor<AuthenticationContributor>();
  64 +
  65 + Context.Request.Headers.Add("Authorization", "BASIC anythinghere");
  66 +
  67 + // when
  68 + var result = when_sending_notification<KnownStages.IHandlerSelection>();
  69 +
  70 + // then
  71 + Context.Response.Headers["Warning"].ShouldBe("Unsupported Authentication Scheme");
  72 + result.ShouldBe(PipelineContinuation.Continue);
  73 + }
  74 +
  75 + [Test]
  76 + public void AuthHeaderWithMalformedHeader()
  77 + {
  78 + // given
  79 + given_pipeline_contributor<AuthenticationContributor>();
  80 +
  81 + var mockScheme = new Mock<IAuthenticationScheme>();
  82 +
  83 + mockScheme.ExpectGet(s => s.Name).Returns("BASIC");
  84 +
  85 + mockScheme
  86 + .Expect(s => s.Authenticate(It.IsAny<IRequest>()))
  87 + .Returns(new AuthenticationResult.MalformedCredentials());
  88 +
  89 + given_dependency(mockScheme.Object);
  90 +
  91 + Context.Request.Headers.Add("Authorization", "BASIC anythinghere");
  92 +
  93 + // when
  94 + var result = when_sending_notification<KnownStages.IHandlerSelection>();
  95 +
  96 + // then
  97 + Context.Response.Headers["Warning"].ShouldBe("Malformed credentials");
  98 + Context.OperationResult.ShouldBeOfType<OperationResult.BadRequest>();
  99 + result.ShouldBe(PipelineContinuation.RenderNow);
  100 + }
  101 +
  102 + [Test]
  103 + public void AuthHeaderWithInvalidCredentials()
  104 + {
  105 + // given
  106 + given_pipeline_contributor<AuthenticationContributor>();
  107 +
  108 + var mockScheme = new Mock<IAuthenticationScheme>();
  109 +
  110 + mockScheme.ExpectGet(s => s.Name).Returns("BASIC");
  111 +
  112 + mockScheme
  113 + .Expect(s => s.Authenticate(It.IsAny<IRequest>()))
  114 + .Returns(new AuthenticationResult.Failed());
  115 +
  116 + given_dependency(mockScheme.Object);
  117 +
  118 + Context.Request.Headers.Add("Authorization", "BASIC anythinghere");
  119 +
  120 + // when
  121 + var result = when_sending_notification<KnownStages.IHandlerSelection>();
  122 +
  123 + // then
  124 + Context.OperationResult.ShouldBeOfType<OperationResult.Unauthorized>();
  125 + result.ShouldBe(PipelineContinuation.Continue);
  126 + }
  127 +
  128 + [Test]
  129 + public void AuthHeaderWithValidCredentials()
  130 + {
  131 + // given
  132 + given_pipeline_contributor<AuthenticationContributor>();
  133 +
  134 + var mockScheme = new Mock<IAuthenticationScheme>();
  135 +
  136 + mockScheme.ExpectGet(s => s.Name).Returns("BASIC");
  137 +
  138 + var username = "someUsername";
  139 + var roles = new[] { "role1", "role2" };
  140 +
  141 + mockScheme
  142 + .Expect(s => s.Authenticate(It.IsAny<IRequest>()))
  143 + .Returns(new AuthenticationResult.Success(username, roles));
  144 +
  145 + given_dependency(mockScheme.Object);
  146 +
  147 + Context.Request.Headers.Add("Authorization", "BASIC anythinghere");
  148 +
  149 + // when
  150 + var result = when_sending_notification<KnownStages.IHandlerSelection>();
  151 +
  152 + // then
  153 + result.ShouldBe(PipelineContinuation.Continue);
  154 +
  155 + Context.User.Identity.Name.ShouldBe(username);
  156 + Context.User.IsInRole(roles[0]);
  157 + Context.User.IsInRole(roles[1]);
  158 + }
  159 + }
  160 +}
0  ...tors/HandlerMethodFiltersInvoker_Specification.cs → ...tors/HandlerMethodFiltersInvoker_Specification.cs
File renamed without changes
0  ...ontributors/HandlerMethodInvoker_Specification.cs → ...ontributors/HandlerMethodInvoker_Specification.cs
File renamed without changes
0  ...ndlerMethodrequestEntityResolver_Specification.cs → ...ndlerMethodrequestEntityResolver_Specification.cs
File renamed without changes
0  ...ine/Contributors/HandlerResolver_Specification.cs → ...ine/Contributors/HandlerResolver_Specification.cs
File renamed without changes
0  ...Contributors/HttpMethodOverrider_Specification.cs → ...Contributors/HttpMethodOverrider_Specification.cs
File renamed without changes
0  ...tors/InvalidRequestEntityRemover_Specification.cs → ...tors/InvalidRequestEntityRemover_Specification.cs
File renamed without changes
0  ...ors/OperationCreationContributor_Specification.cs → ...ors/OperationCreationContributor_Specification.cs
File renamed without changes
0  ...Contributors/OperationProcessors_Specification.cs → ...Contributors/OperationProcessors_Specification.cs
File renamed without changes
0  ...Contributors/RequestEntityReader_Specification.cs → ...Contributors/RequestEntityReader_Specification.cs
File renamed without changes
0  ...ontributors/ResourceTypeResolver_Specification.cs → ...ontributors/ResourceTypeResolver_Specification.cs
File renamed without changes
0  ...tors/ResponseEntityCodecResolver_Specification.cs → ...tors/ResponseEntityCodecResolver_Specification.cs
File renamed without changes
0  ...ontributors/ResponseEntityWriter_Specification.cs → ...ontributors/ResponseEntityWriter_Specification.cs
File renamed without changes
0  ...ributors/UriDecoratorsController_Specification.cs → ...ributors/UriDecoratorsController_Specification.cs
File renamed without changes
0  ...Pipeline/DigestCredentialsReader_Specification.cs → ...Pipeline/DigestCredentialsReader_Specification.cs
File renamed without changes
0  ...Unit/Web/Pipeline/PipelineRunner_Specification.cs → ...sts.Unit/Pipeline/PipelineRunner_Specification.cs
File renamed without changes
30 src/core/OpenRasta.Tests.Unit/openrasta_context.cs
@@ -56,6 +56,12 @@ protected IUriResolver UriResolver
56 56 get { return Resolver.Resolve<IUriResolver>(); }
57 57 }
58 58
  59 + public void given_dependency<TInterface>(TInterface instance)
  60 + {
  61 + Resolver.AddDependencyInstance(typeof(TInterface), instance, DependencyLifetime.Singleton);
  62 +
  63 + }
  64 +
59 65 public T given_pipeline_contributor<T>() where T : class, IPipelineContributor
60 66 {
61 67 return given_pipeline_contributor<T>(null);
@@ -189,11 +195,6 @@ protected void given_response_entity(object responseEntity)
189 195 Context.OperationResult = new OperationResult.OK { ResponseResource = responseEntity };
190 196 }
191 197
192   - protected void GivenAUser(string username, string password)
193   - {
194   - var provider = Resolver.Resolve<IAuthenticationProvider>() as InMemAuthenticationProvider;
195   - provider.Passwords[username] = password;
196   - }
197 198 protected TestErrorCollector Errors { get; private set; }
198 199 protected override void SetUp()
199 200 {
@@ -202,8 +203,6 @@ protected override void SetUp()
202 203 Pipeline = null;
203 204 _actions = new Dictionary<Type, Func<ICommunicationContext, PipelineContinuation>>();
204 205 var manager = Host.HostManager;
205   - if (!Resolver.HasDependency(typeof(IAuthenticationProvider)))
206   - Resolver.AddDependency<IAuthenticationProvider, InMemAuthenticationProvider>();
207 206 Resolver.AddDependencyInstance(typeof(IErrorCollector), Errors = new TestErrorCollector());
208 207 Resolver.AddDependency<IPathManager, PathManager>();
209 208
@@ -217,23 +216,6 @@ protected override void TearDown()
217 216 DependencyManager.UnsetResolver();
218 217 }
219 218
220   - public class InMemAuthenticationProvider : IAuthenticationProvider
221   - {
222   - public Dictionary<string, string> Passwords = new Dictionary<string, string>();
223   -
224   - public Credentials GetByUsername(string username)
225   - {
226   - if (username == null || !Passwords.ContainsKey(username))
227   - return null;
228   - return new Credentials
229   - {
230   - Username = username,
231   - Password = Passwords[username],
232   - Roles = new string[0]
233   - };
234   - }
235   - }
236   -
237 219 public class SinglePipeline<T> : IPipeline, IPipelineExecutionOrder, IPipelineExecutionOrderAnd where T : class, IPipelineContributor
238 220 {
239 221 internal Dictionary<Type, Func<ICommunicationContext, PipelineContinuation>> _actions;
26 src/core/OpenRasta/Authentication/AuthenticationResult.cs
... ... @@ -0,0 +1,26 @@
  1 +using System;
  2 +using System.Collections.Generic;
  3 +using System.Security.Principal;
  4 +using OpenRasta.Pipeline.Contributors;
  5 +
  6 +namespace OpenRasta.Authentication
  7 +{
  8 + public class AuthenticationResult
  9 + {
  10 + public class MalformedCredentials : AuthenticationResult { }
  11 +
  12 + public class Failed : AuthenticationResult { }
  13 +
  14 + public class Success : AuthenticationResult
  15 + {
  16 + public string Username { get; private set; }
  17 + public string[] Roles { get; private set; }
  18 +
  19 + public Success(string username, params string[] roles)
  20 + {
  21 + Username = username;
  22 + Roles = roles;
  23 + }
  24 + }
  25 + }
  26 +}
18 src/core/OpenRasta/Authentication/Basic/BasicAuthRequestHeader.cs
... ... @@ -0,0 +1,18 @@
  1 +using System;
  2 +using System.Text;
  3 +using OpenRasta;
  4 +
  5 +namespace OpenRasta.Authentication.Basic
  6 +{
  7 + public class BasicAuthRequestHeader
  8 + {
  9 + public string Username { get; private set; }
  10 + public string Password { get; private set; }
  11 +
  12 + internal BasicAuthRequestHeader(string username, string password)
  13 + {
  14 + Username = username;
  15 + Password = password;
  16 + }
  17 + }
  18 +}
55 src/core/OpenRasta/Authentication/Basic/BasicAuthenticationScheme.cs
... ... @@ -0,0 +1,55 @@
  1 +using OpenRasta.Web;
  2 +
  3 +namespace OpenRasta.Authentication.Basic
  4 +{
  5 + public class BasicAuthenticationScheme : IAuthenticationScheme
  6 + {
  7 + const string SCHEME = "Basic";
  8 +
  9 + private readonly IBasicAuthenticator _basicAuthenticator;
  10 +
  11 + public string Name { get { return SCHEME; } }
  12 +
  13 + public BasicAuthenticationScheme(IBasicAuthenticator basicAuthenticator)
  14 + {
  15 + _basicAuthenticator = basicAuthenticator;
  16 + }
  17 +
  18 + public AuthenticationResult Authenticate(IRequest request)
  19 + {
  20 + BasicAuthRequestHeader credentials = ExtractBasicHeader(request.Headers["Authorization"]);
  21 +
  22 + if (credentials != null)
  23 + {
  24 + return _basicAuthenticator.Authenticate(credentials);
  25 + }
  26 +
  27 + return new AuthenticationResult.MalformedCredentials();
  28 + }
  29 +
  30 + public void Challenge(IResponse response)
  31 + {
  32 + response.Headers["WWW-Authenticate"] = string.Format("{0} realm=\"{1}\"", SCHEME, _basicAuthenticator.Realm);
  33 + }
  34 +
  35 + internal static BasicAuthRequestHeader ExtractBasicHeader(string value)
  36 + {
  37 + try
  38 + {
  39 + var basicBase64Credentials = value.Split(' ')[1];
  40 +
  41 + var basicCredentials = basicBase64Credentials.FromBase64String().Split(':');
  42 +
  43 + if (basicCredentials.Length != 2)
  44 + return null;
  45 +
  46 + return new BasicAuthRequestHeader(basicCredentials[0], basicCredentials[1]);
  47 + }
  48 + catch
  49 + {
  50 + return null;
  51 + }
  52 +
  53 + }
  54 + }
  55 +}
8 src/core/OpenRasta/Authentication/Basic/IBasicAuthenticator.cs
... ... @@ -0,0 +1,8 @@
  1 +namespace OpenRasta.Authentication.Basic
  2 +{
  3 + public interface IBasicAuthenticator
  4 + {
  5 + string Realm { get; }
  6 + AuthenticationResult Authenticate(BasicAuthRequestHeader header);
  7 + }
  8 +}
156 src/core/OpenRasta/Authentication/Digest/DigestAuthRequestHeader.cs
... ... @@ -0,0 +1,156 @@
  1 +using System;
  2 +using System.Security.Cryptography;
  3 +using System.Text;
  4 +using OpenRasta;
  5 +
  6 +namespace OpenRasta.Authentication.Digest
  7 +{
  8 + public enum DigestAlgorithm
  9 + {
  10 + MD5
  11 + }
  12 +
  13 + public class DigestAuthResponseChallenge
  14 + {
  15 + public string Realm { get; private set; } // realm=""
  16 +
  17 + public string Username { get; private set; } // qop=""
  18 + public string Password { get; private set; } // qop=""
  19 + public string Salt { get; private set; } // qop=""
  20 + public string QualityOfProtection { get; private set; } // qop=""
  21 + public string ServerNonce { get; private set; } // nonce=""
  22 + public string ClientNonce { get; private set; } // cnonce=""
  23 + public string Uri { get; private set; } // uri=""
  24 + public string Response { get; private set; } // response=""
  25 + public string Digest { get; private set; } // digest=""
  26 + public string Opaque { get; private set; } // opaque=""
  27 + public string RequestCounter { get; private set; } // nc=""
  28 + public bool Stale { get; private set; } // stale=""
  29 + public DigestAlgorithm Algorithm { get; private set; } // algorithm=""
  30 +
  31 + public DigestAuthResponseChallenge(string realm, string serverNonce, byte[] opaqueData, bool stale)
  32 + {
  33 +
  34 + }
  35 +
  36 + public string GetCalculatedResponse(string httpMethod)
  37 + {
  38 + // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
  39 + string A1 = String.Format("{0}:{1}:{2}", Username, Realm, Password);
  40 +
  41 + // H(A1) = MD5(A1)
  42 + string HA1 = GetMD5HashBinHex(A1);
  43 +
  44 + // A2 = Method ":" digest-uri-value
  45 + string A2 = String.Format("{0}:{1}", httpMethod, Uri);
  46 +
  47 + // H(A2)
  48 + string HA2 = GetMD5HashBinHex(A2);
  49 +
  50 + // KD(secret, data) = H(concat(secret, ":", data))
  51 + // if qop == auth:
  52 + // request-digest = <"> < KD ( H(A1), unq(nonce-value)
  53 + // ":" nc-value
  54 + // ":" unq(cnonce-value)
  55 + // ":" unq(qop-value)
  56 + // ":" H(A2)
  57 + // ) <">
  58 + // if qop is missing,
  59 + // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
  60 + string unhashedDigest;
  61 + if (QualityOfProtection != null)
  62 + {
  63 + unhashedDigest = String.Format("{0}:{1}:{2}:{3}:{4}:{5}",
  64 + HA1,
  65 + ServerNonce,
  66 + RequestCounter,
  67 + ClientNonce,
  68 + QualityOfProtection,
  69 + HA2);
  70 + }
  71 + else
  72 + {
  73 + unhashedDigest = String.Format("{0}:{1}:{2}",
  74 + HA1,
  75 + RequestCounter,
  76 + HA2);
  77 + }
  78 +
  79 + return GetMD5HashBinHex(unhashedDigest);
  80 + }
  81 +
  82 + static string GetMD5HashBinHex(string toBeHashed)
  83 + {
  84 + MD5 hash = MD5.Create();
  85 + byte[] result = hash.ComputeHash(Encoding.ASCII.GetBytes(toBeHashed));
  86 +
  87 + var sb = new StringBuilder();
  88 + foreach (byte b in result)
  89 + sb.Append(b.ToString("x2"));
  90 + return sb.ToString();
  91 + }
  92 + }
  93 +
  94 + public class DigestAuthRequestParameters
  95 + {
  96 + private const string SchemeName = "DIGEST";
  97 + private const string SchemeNameWithSpace = SchemeName + " ";
  98 +
  99 + public string Username { get; private set; } // username=""
  100 + public string Realm { get; private set; } // realm=""
  101 +
  102 + public string QualityOfProtection { get; private set; } // qop=""
  103 + public string ServerNonce { get; private set; } // nonce=""
  104 + public string ClientNonce { get; private set; } // cnonce=""
  105 + public string Uri { get; private set; } // uri=""
  106 + public string Response { get; private set; } // response=""
  107 + public string Digest { get; private set; } // digest=""
  108 + public string Opaque { get; private set; } // opaque=""
  109 + public string RequestCounter { get; private set; } // nc=""
  110 +
  111 + public DigestAuthRequestParameters(string username)
  112 + {
  113 + Username = username;
  114 + }
  115 +
  116 + public static DigestAuthRequestParameters Parse(string value)
  117 + {
  118 + if (value.IsNullOrWhiteSpace()) return null;
  119 +
  120 + if (!value.ToUpper().StartsWith(SchemeNameWithSpace)) return null;
  121 +
  122 + var basicBase64Credentials = value.Split(' ')[1];
  123 + var basicCredentials = Encoding.UTF8.GetString(Convert.FromBase64String(basicBase64Credentials)).Split(':');
  124 +
  125 + var username = basicCredentials[0];
  126 + var password = basicCredentials[1];
  127 +
  128 + return new DigestAuthRequestParameters(username, password);
  129 + }
  130 +
  131 + public static bool TryParse(string value, out DigestAuthRequestParameters credentials)
  132 + {
  133 + credentials = null;
  134 +
  135 + if (string.IsNullOrWhiteSpace(value)) return false;
  136 +
  137 + if (!value.ToUpper().StartsWith(SchemeNameWithSpace)) return false;
  138 +
  139 + var basicBase64Credentials = value.Split(' ')[1];
  140 +
  141 + credentials = ExtractBasicCredentials(basicBase64Credentials);
  142 +
  143 + return true;
  144 + }
  145 +
  146 + private static DigestAuthRequestParameters ExtractDigestCredentials(string basicCredentialsAsBase64)
  147 + {
  148 + var basicCredentials = basicCredentialsAsBase64.FromBase64String().Split(':');
  149 +
  150 + var username = basicCredentials[0];
  151 + var password = basicCredentials[1];
  152 +
  153 + return new DigestAuthRequestParameters(username, password);
  154 + }
  155 + }
  156 +}
34 src/core/OpenRasta/Authentication/Digest/DigestAuthenticationScheme.cs
... ... @@ -0,0 +1,34 @@
  1 +using OpenRasta.Authentication.Basic;
  2 +using OpenRasta.Web;
  3 +
  4 +namespace OpenRasta.Authentication.Digest
  5 +{
  6 + public class DigestAuthenticationScheme : IAuthenticationScheme
  7 + {
  8 + private readonly IDigestAuthenticator _digestAuthenticator;
  9 +
  10 + public string Name { get { return "Basic"; } }
  11 +
  12 + public DigestAuthenticationScheme(IDigestAuthenticator digestAuthenticator)
  13 + {
  14 + _digestAuthenticator = digestAuthenticator;
  15 + }
  16 +
  17 + public AuthenticationResult Authenticate(IRequest request)
  18 + {
  19 + DigestAuthRequestParameters credentials;
  20 +
  21 + if (DigestAuthRequestParameters.TryParse(request.Headers["Authorization"], out credentials))
  22 + {
  23 + return _digestAuthenticator.Authenticate(credentials);
  24 + }
  25 +
  26 + return new AuthenticationResult.MalformedCredentials();
  27 + }
  28 +
  29 + public void Challenge(IResponse response)
  30 + {
  31 + response.Headers["WWW-Authenticate"] = string.Format("Basic realm=\"{0}\"", _digestAuthenticator.Realm);
  32 + }
  33 + }
  34 +}
8 src/core/OpenRasta/Authentication/Digest/IDigestAuthenticator.cs
... ... @@ -0,0 +1,8 @@
  1 +namespace OpenRasta.Authentication.Digest
  2 +{
  3 + public interface IDigestAuthenticator
  4 + {
  5 + string Realm { get; }
  6 + AuthenticationResult Authenticate(DigestAuthRequestParameters header);
  7 + }
  8 +}
13 src/core/OpenRasta/Authentication/IAuthenticationScheme.cs
... ... @@ -0,0 +1,13 @@
  1 +using OpenRasta.Web;
  2 +
  3 +namespace OpenRasta.Authentication
  4 +{
  5 + public interface IAuthenticationScheme
  6 + {
  7 + string Name { get; }
  8 +
  9 + AuthenticationResult Authenticate(IRequest request);
  10 +
  11 + void Challenge(IResponse response);
  12 + }
  13 +}
3  src/core/OpenRasta/Configuration/DefaultDependencyRegistrar.cs
@@ -346,7 +346,8 @@ void AddDefaultContributors()
346 346 AddPipelineContributor<ResourceTypeResolverContributor>();
347 347 AddPipelineContributor<HandlerResolverContributor>();
348 348
349   - AddPipelineContributor<DigestAuthorizerContributor>();
  349 + AddPipelineContributor<AuthenticationContributor>();
  350 + AddPipelineContributor<AuthenticationChallengerContributor>();
350 351
351 352 AddPipelineContributor<OperationCreatorContributor>();
352 353 AddPipelineContributor<OperationFilterContributor>();
27 src/core/OpenRasta/OpenRasta.csproj
@@ -60,6 +60,11 @@
60 60 <Compile Include="..\..\CommonInfo.cs">
61 61 <Link>Properties\CommonInfo.cs</Link>
62 62 </Compile>
  63 + <Compile Include="Authentication\AuthenticationResult.cs" />
  64 + <Compile Include="Authentication\Basic\BasicAuthenticationScheme.cs" />
  65 + <Compile Include="Authentication\Basic\BasicAuthRequestHeader.cs" />
  66 + <Compile Include="Authentication\Basic\IBasicAuthenticator.cs" />
  67 + <Compile Include="Authentication\IAuthenticationScheme.cs" />
63 68 <Compile Include="Binding\BinderAttribute.cs" />
64 69 <Compile Include="Binding\DefaultObjectBinderLocator.cs" />
65 70 <Compile Include="Binding\BindingResult.cs" />
@@ -122,6 +127,11 @@
122 127 <Compile Include="Diagnostics\LogSource.cs" />
123 128 <Compile Include="Diagnostics\NullErrorCollector.cs" />
124 129 <Compile Include="Diagnostics\OperationContextErrorCollector.cs" />
  130 + <Compile Include="Pipeline\Contributors\AuthenticationChallengerContributor.cs" />
  131 + <Compile Include="Pipeline\Contributors\AuthenticationContributor.cs" />
  132 + <Compile Include="Pipeline\Contributors\DigestAuthorizerContributor.cs">
  133 + <SubType>Code</SubType>
  134 + </Compile>
125 135 <Compile Include="Pipeline\Diagnostics\PipelineLogSource.cs" />
126 136 <Compile Include="DI\Internal\ContextStoreDependency.cs" />
127 137 <Compile Include="DI\Internal\ContextStoreExtensions.cs" />
@@ -142,7 +152,8 @@
142 152 <Compile Include="Configuration\MetaModel\Handlers\DependencyRegistrationModelHandler.cs" />
143 153 <Compile Include="Handlers\HandlerRepository.cs" />
144 154 <Compile Include="Handlers\IHandlerRepository.cs" />
145   - <Compile Include="Configuration\Fluent\ICodecDefinition.cs" /> <Compile Include="Configuration\Fluent\ICodecParentDefinition.cs" />
  155 + <Compile Include="Configuration\Fluent\ICodecDefinition.cs" />
  156 + <Compile Include="Configuration\Fluent\ICodecParentDefinition.cs" />
146 157 <Compile Include="Configuration\Fluent\ICodecWithMediaTypeDefinition.cs" />
147 158 <Compile Include="Configuration\Fluent\IHandlerForResourceWithUriDefinition.cs" />
148 159 <Compile Include="Configuration\Fluent\IHandlerParentDefinition.cs" />
@@ -215,6 +226,16 @@
215 226 <Compile Include="OperationModel\OutputMember.cs" />
216 227 <Compile Include="Pipeline\IContextStore.cs" />
217 228 <Compile Include="Pipeline\IContextStoreDependencyCleaner.cs" />
  229 + <Compile Include="Security\Credentials.cs">
  230 + <SubType>Code</SubType>
  231 + </Compile>
  232 + <Compile Include="Security\DigestHeader.cs">
  233 + <SubType>Code</SubType>
  234 + </Compile>
  235 + <Compile Include="Security\IAuthenticationProvider.cs">
  236 + <SubType>Code</SubType>
  237 + </Compile>
  238 + <Compile Include="Security\RequiresAuthenticationInterceptor.cs" />
218 239 <Compile Include="Security\RequiresRoleAttribute.cs" />
219 240 <Compile Include="Security\RequiresRoleInterceptor.cs" />
220 241 <Compile Include="TypeSystem\DebuggerStrings.cs" />
@@ -411,9 +432,6 @@
411 432 <Compile Include="IO\ByteArrayExtension.cs" />
412 433 <Compile Include="IO\HistoryStream.cs" />
413 434 <Compile Include="IO\StreamExtensions.cs" />
414   - <Compile Include="Security\DigestHeader.cs" />
415   - <Compile Include="Security\IAuthenticationProvider.cs" />
416   - <Compile Include="Security\Credentials.cs" />
417 435 <Compile Include="UriTemplate.cs" />
418 436 <Compile Include="Properties\AssemblyInfo.cs" />
419 437 <Compile Include="UriTemplateEqualityComparer.cs" />
@@ -431,7 +449,6 @@
431 449 <Compile Include="Pipeline\ContributorCall.cs" />
432 450 <Compile Include="Pipeline\Contributors\AbstractOperationProcessing.cs" />
433 451 <Compile Include="Pipeline\Contributors\BootstrapperContributor.cs" />
434   - <Compile Include="Pipeline\Contributors\DigestAuthorizerContributor.cs" />
435 452 <Compile Include="OperationModel\IOperationCreator.cs" />
436 453 <Compile Include="OperationModel\IOperation.cs" />
437 454 <Compile Include="OperationModel\IOperationFilter.cs" />
41 src/core/OpenRasta/Pipeline/Contributors/AuthenticationChallengerContributor.cs
... ... @@ -0,0 +1,41 @@
  1 +using OpenRasta.Authentication;
  2 +using OpenRasta.DI;
  3 +using OpenRasta.Diagnostics;
  4 +using OpenRasta.Web;
  5 +
  6 +namespace OpenRasta.Pipeline.Contributors
  7 +{