diff --git a/src/OpenRasta.Tests.Unit/OpenRasta.Tests.Unit.csproj b/src/OpenRasta.Tests.Unit/OpenRasta.Tests.Unit.csproj
index f8f43a24..3422284e 100644
--- a/src/OpenRasta.Tests.Unit/OpenRasta.Tests.Unit.csproj
+++ b/src/OpenRasta.Tests.Unit/OpenRasta.Tests.Unit.csproj
@@ -127,6 +127,7 @@
+
diff --git a/src/OpenRasta.Tests.Unit/Pipeline/Contributors/BasicAuthorizer_Specification.cs b/src/OpenRasta.Tests.Unit/Pipeline/Contributors/BasicAuthorizer_Specification.cs
index 26905b8f..923f5e50 100644
--- a/src/OpenRasta.Tests.Unit/Pipeline/Contributors/BasicAuthorizer_Specification.cs
+++ b/src/OpenRasta.Tests.Unit/Pipeline/Contributors/BasicAuthorizer_Specification.cs
@@ -36,61 +36,5 @@ public void BasicAuthorizer_Succeeds_For_Valid_Credentials(string username, stri
result.ShouldBe(PipelineContinuation.Continue);
Context.User.Identity.Name.ShouldBe(username);
}
-
- [Test]
- public void BasicAuthorizer_Returns_Authenticate_Header_For_Invalid_Credentials()
- {
- var mockAuthenticationProvider = new Mock();
- mockAuthenticationProvider.Setup(ap => ap.GetByUsername(It.IsAny())).Returns(new Credentials());
- mockAuthenticationProvider.Setup(ap => ap.ValidatePassword(It.IsAny(), It.IsAny())).Returns(false);
- given_dependency(mockAuthenticationProvider.Object);
- given_pipeline_contributor();
- Context.Request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("username:password")));
- var result = when_sending_notification();
- result.ShouldBe(PipelineContinuation.RenderNow);
- Context.OperationResult.ShouldBeOfType();
- Context.User.ShouldBe(null);
- }
-
- [Test]
- [TestCase("Basic THIS_IS_NOT_A_BASE64_ENCODED_CREDENTIAL")]
- [TestCase("Invalid Header")]
- public void BasicAuthorizer_Returns_Unauthorized_When_Header_Is_Invalid(string header)
- {
- var mockAuthenticationProvider = new Mock();
- given_dependency(mockAuthenticationProvider.Object);
- given_pipeline_contributor();
-
- Context.Request.Headers.Add("Authorization", header);
-
- var result = when_sending_notification();
- result.ShouldBe(PipelineContinuation.RenderNow);
- Context.OperationResult.ShouldBeOfType();
- }
-
- [Test]
- public void BasicAuthorizer_Sends_WwwAuthenticate_For_Missing_Credentials()
- {
- var mockAuthenticationProvider = new Mock();
- given_dependency(mockAuthenticationProvider.Object);
- given_pipeline_contributor();
-
- when_sending_notification();
- when_sending_notification();
- var header = String.Format("Basic realm=\"{0}\"", BasicAuthenticationRequiredHeader.DEFAULT_REALM);
- Context.Response.Headers["WWW-Authenticate"].ShouldBe(header);
- }
-
- [Test]
- public void BasicAuthorizer_Returns_NotAuthorized_For_Missing_Credentials()
- {
- var mockAuthenticationProvider = new Mock();
- given_dependency(mockAuthenticationProvider.Object);
- given_pipeline_contributor();
-
- var result = when_sending_notification();
- result.ShouldBe(PipelineContinuation.RenderNow);
- Context.OperationResult.ShouldBeOfType();
- }
}
}
diff --git a/src/OpenRasta.Tests.Unit/Security/RequiresAuthenticationInterceptor_Specification.cs b/src/OpenRasta.Tests.Unit/Security/RequiresAuthenticationInterceptor_Specification.cs
index faf9ab82..be66e819 100644
--- a/src/OpenRasta.Tests.Unit/Security/RequiresAuthenticationInterceptor_Specification.cs
+++ b/src/OpenRasta.Tests.Unit/Security/RequiresAuthenticationInterceptor_Specification.cs
@@ -38,4 +38,5 @@ public void execution_is_allowed()
.ShouldBeTrue();
}
}
-}
\ No newline at end of file
+}
+
diff --git a/src/OpenRasta.Tests.Unit/Security/RequiresBasicAuthenticationInterceptor_Specification.cs b/src/OpenRasta.Tests.Unit/Security/RequiresBasicAuthenticationInterceptor_Specification.cs
new file mode 100644
index 00000000..734b3fd2
--- /dev/null
+++ b/src/OpenRasta.Tests.Unit/Security/RequiresBasicAuthenticationInterceptor_Specification.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Diagnostics;
+using System.Security.Principal;
+using Moq;
+using NUnit.Framework;
+using OpenRasta.Hosting.InMemory;
+using OpenRasta.OperationModel;
+using OpenRasta.Security;
+using OpenRasta.Testing;
+using OpenRasta.Web;
+
+namespace RequiresBasicAuthenticationInterceptor_Specification
+{
+ public class when_the_user_is_not_authenticated : context
+ {
+ [Test]
+ public void execution_is_denied()
+ {
+ var context = new InMemoryCommunicationContext();
+ const string REALM = "Test Realm";
+ var authenticationInterceptor = new RequiresBasicAuthenticationInterceptor(context, REALM);
+ authenticationInterceptor.BeforeExecute(new Mock().Object).ShouldBeFalse();
+ context.OperationResult.ShouldBeOfType();
+ var expectedHeader = String.Format("Basic realm=\"{0}\"", REALM);
+ context.Response.Headers["WWW-Authenticate"].ShouldBe(expectedHeader);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OpenRasta/OpenRasta.csproj b/src/OpenRasta/OpenRasta.csproj
index d06a17f2..75730479 100644
--- a/src/OpenRasta/OpenRasta.csproj
+++ b/src/OpenRasta/OpenRasta.csproj
@@ -329,6 +329,8 @@
+
+
diff --git a/src/OpenRasta/Pipeline/Contributors/BasicAuthorizerContributor.cs b/src/OpenRasta/Pipeline/Contributors/BasicAuthorizerContributor.cs
index d5473525..ece0cd49 100644
--- a/src/OpenRasta/Pipeline/Contributors/BasicAuthorizerContributor.cs
+++ b/src/OpenRasta/Pipeline/Contributors/BasicAuthorizerContributor.cs
@@ -26,8 +26,6 @@ namespace OpenRasta.Pipeline.Contributors
{
public class BasicAuthorizerContributor : IPipelineContributor
{
- public const string REALM = "Basic Authentication";
-
private readonly IDependencyResolver _resolver;
private IAuthenticationProvider _authentication;
@@ -43,72 +41,39 @@ public void Initialize(IPipeline pipelineRunner)
.After()
.And
.Before();
-
- pipelineRunner.Notify(WriteCredentialRequest)
- .After()
- .And
- .Before();
}
public PipelineContinuation ReadCredentials(ICommunicationContext context)
{
- if (!_resolver.HasDependency(typeof(IAuthenticationProvider)))
- {
- return NotAuthorized(context);
- }
-
- _authentication = _resolver.Resolve();
-
- try
+ if (_resolver.HasDependency(typeof(IAuthenticationProvider)))
{
+ _authentication = _resolver.Resolve();
var header = ReadBasicAuthHeader(context);
-
- if (header == null)
- {
- return NotAuthorized(context);
- }
-
- var credentials = _authentication.GetByUsername(header.Username);
-
- if (credentials == null)
+ if (header != null)
{
- return NotAuthorized(context);
- }
+ var credentials = _authentication.GetByUsername(header.Username);
- if (!_authentication.ValidatePassword(credentials, header.Password))
- {
- return NotAuthorized(context);
+ if (_authentication.ValidatePassword(credentials, header.Password))
+ {
+ IIdentity id = new GenericIdentity(credentials.Username, "Basic");
+ context.User = new GenericPrincipal(id, credentials.Roles);
+ }
}
- IIdentity id = new GenericIdentity(credentials.Username, "Basic");
- context.User = new GenericPrincipal(id, credentials.Roles);
- return PipelineContinuation.Continue;
- }
- catch (ArgumentException ex)
- {
- return NotAuthorized(context);
}
- }
-
- private static BasicAuthorizationHeader ReadBasicAuthHeader(ICommunicationContext context)
- {
- var header = context.Request.Headers["Authorization"];
- return string.IsNullOrEmpty(header) ? null : BasicAuthorizationHeader.Parse(header);
- }
- private static PipelineContinuation NotAuthorized(ICommunicationContext context)
- {
- context.OperationResult = new OperationResult.Unauthorized();
- return PipelineContinuation.RenderNow;
+ return PipelineContinuation.Continue;
}
- private static PipelineContinuation WriteCredentialRequest(ICommunicationContext context)
+ private static BasicAuthorizationHeader ReadBasicAuthHeader(ICommunicationContext context)
{
- if (context.OperationResult is OperationResult.Unauthorized)
+ try
+ {
+ var header = context.Request.Headers["Authorization"];
+ return string.IsNullOrEmpty(header) ? null : BasicAuthorizationHeader.Parse(header);
+ } catch (ArgumentException ex)
{
- var header = new BasicAuthenticationRequiredHeader().ServerResponseHeader;
- context.Response.Headers["WWW-Authenticate"] = header;
+ return (null);
}
- return PipelineContinuation.Continue;
}
}
}
diff --git a/src/OpenRasta/Security/BasicAuthorizationHeader.cs b/src/OpenRasta/Security/BasicAuthorizationHeader.cs
index f32050a0..de2a697d 100644
--- a/src/OpenRasta/Security/BasicAuthorizationHeader.cs
+++ b/src/OpenRasta/Security/BasicAuthorizationHeader.cs
@@ -30,13 +30,9 @@ private BasicAuthorizationHeader(string username, string password)
public static BasicAuthorizationHeader Parse(string header)
{
var tokens = header.Split(' ');
- if (tokens.Length != 2)
+ if (tokens.Length != 2 || tokens[0] != "Basic")
{
- throw (new ArgumentException("Supplied header is not in the format Basic {base64-encoded credential pair}", "header"));
- }
- if (tokens[0] != "Basic")
- {
- throw (new ArgumentException("Supplied header is not an HTTP Basic authorization header", header));
+ return (null);
}
try
{
@@ -46,7 +42,7 @@ public static BasicAuthorizationHeader Parse(string header)
return (new BasicAuthorizationHeader(credentials[0], credentials[1]));
} catch (FormatException ex)
{
- throw (new ArgumentException("Supplied header doesn't contain valid base64 encoded credentials", ex));
+ return (null);
}
}
}
diff --git a/src/OpenRasta/Security/RequiresAuthenticationInterceptor.cs b/src/OpenRasta/Security/RequiresAuthenticationInterceptor.cs
index 5cac10c7..64ef4d85 100644
--- a/src/OpenRasta/Security/RequiresAuthenticationInterceptor.cs
+++ b/src/OpenRasta/Security/RequiresAuthenticationInterceptor.cs
@@ -17,11 +17,18 @@ public override bool BeforeExecute(IOperation operation)
{
if (_context.User == null || _context.User.Identity == null || !_context.User.Identity.IsAuthenticated)
{
- _context.OperationResult = new OperationResult.Unauthorized();
+ DenyAuthorization(_context);
return false;
}
return true;
}
+
+ protected virtual void DenyAuthorization(ICommunicationContext context)
+ {
+ _context.OperationResult = new OperationResult.Unauthorized();
+ }
}
+
+
}
\ No newline at end of file
diff --git a/src/OpenRasta/Security/RequiresBasicAuthenticationAttribute.cs b/src/OpenRasta/Security/RequiresBasicAuthenticationAttribute.cs
new file mode 100644
index 00000000..34fb642c
--- /dev/null
+++ b/src/OpenRasta/Security/RequiresBasicAuthenticationAttribute.cs
@@ -0,0 +1,57 @@
+#region License
+/* Authors:
+ * Sebastien Lambla (seb@serialseb.com)
+ * Copyright:
+ * (C) 2007-2009 Caffeine IT & naughtyProd Ltd (http://www.caffeine-it.com)
+ * License:
+ * This file is distributed under the terms of the MIT License found at the end of this file.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using OpenRasta.DI;
+using OpenRasta.OperationModel;
+using OpenRasta.OperationModel.Interceptors;
+using OpenRasta.Web;
+
+namespace OpenRasta.Security
+{
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
+ public class RequiresBasicAuthenticationAttribute : InterceptorProviderAttribute
+ {
+ private readonly string realm;
+
+ public RequiresBasicAuthenticationAttribute(string realm)
+ {
+ this.realm = realm;
+ }
+
+ public override IEnumerable GetInterceptors(IOperation operation)
+ {
+ return new[]
+ {
+ new RequiresBasicAuthenticationInterceptor(DependencyManager.GetService(),realm)
+ };
+ }
+ }
+}
+
+#region Full license
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#endregion
\ No newline at end of file
diff --git a/src/OpenRasta/Security/RequiresBasicAuthenticationInterceptor.cs b/src/OpenRasta/Security/RequiresBasicAuthenticationInterceptor.cs
new file mode 100644
index 00000000..aac3e68b
--- /dev/null
+++ b/src/OpenRasta/Security/RequiresBasicAuthenticationInterceptor.cs
@@ -0,0 +1,25 @@
+using OpenRasta.OperationModel;
+using OpenRasta.OperationModel.Interceptors;
+using OpenRasta.Pipeline;
+using OpenRasta.Web;
+
+namespace OpenRasta.Security
+{
+ public class RequiresBasicAuthenticationInterceptor : RequiresAuthenticationInterceptor
+ {
+ private readonly string realm;
+
+ public RequiresBasicAuthenticationInterceptor(ICommunicationContext context, string realm)
+ : base(context)
+ {
+ this.realm = realm;
+ }
+
+ protected override void DenyAuthorization(ICommunicationContext context)
+ {
+ base.DenyAuthorization(context);
+ var header = new BasicAuthenticationRequiredHeader(realm).ServerResponseHeader;
+ context.Response.Headers["WWW-Authenticate"] = header;
+ }
+ }
+}