Skip to content

Instantly share code, notes, and snippets.

@b1ff
Created April 23, 2014 09:36
Show Gist options
  • Save b1ff/11208757 to your computer and use it in GitHub Desktop.
Save b1ff/11208757 to your computer and use it in GitHub Desktop.
[TestFixture]
public class FormsAuthenticationFilterTests
{
private class TestController : ApiController
{
[AllowAnonymous]
public void PublicMethod()
{
}
public void MethodWithAuth()
{
}
}
private FormsAuthenticationFilter _filter;
private Mock<HttpContextBase> _httpCtxMock;
private Mock<IFormsAuthWrapper> _formsAuthMock;
private string _defaultTicket;
private const string DefaultUsername = "testuser";
[SetUp]
public void SetUp()
{
_defaultTicket = Guid.NewGuid().ToString("N");
_httpCtxMock = new Mock<HttpContextBase>();
_formsAuthMock = new Mock<IFormsAuthWrapper>();
_filter = new FormsAuthenticationFilter(
_formsAuthMock.Object,
_httpCtxMock.Object
);
}
[Test]
public void OnAuthorization_AuthenticationHeaderIsMissed_SetUnthorizedRequest()
{
var ctx = AuthActionContext();
_filter.OnAuthorization(ctx);
ctx.Response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
}
[Test]
public void OnAuthorization_AuthenticationHeaderIsMissedButActionAllowsAnonymous_DontSetUnotharizedResponse()
{
var ctx = NewActionContext("PublicMethod");
_filter.OnAuthorization(ctx);
ctx.Response.Should().BeNull();
}
[Test]
public void OnAuthorization_AuthenticationHeaderExists_SetPrincipalToHttpCtx()
{
_httpCtxMock.SetupSet(_ => _.User = It.IsAny<IPrincipal>()).Verifiable();
var ctx = SetDefaultAuthData();
SetDefaultTicketToRequest(ctx);
_filter.OnAuthorization(ctx);
_httpCtxMock.VerifySet(
c => c.User = It.Is<IPrincipal>(
p => p.Identity.Name == DefaultUsername
&& p.Identity.IsAuthenticated));
}
[Test]
public void OnAuthorization_AuthenticationCookieExists_SetPrincipalToCurrentThread()
{
var ctx = SetDefaultAuthData();
AddDefaultAuthCookie(ctx);
_filter.OnAuthorization(ctx);
Thread.CurrentPrincipal.Identity.Name.Should().Be(DefaultUsername);
Thread.CurrentPrincipal.Identity.IsAuthenticated.Should().BeTrue();
}
[Test]
public void OnAuthorization_AuthenticationCookieExists_SetPrincipalToHttpCtx()
{
_httpCtxMock.SetupSet(_ => _.User = It.IsAny<IPrincipal>()).Verifiable();
var ctx = SetDefaultAuthData();
AddDefaultAuthCookie(ctx);
_filter.OnAuthorization(ctx);
_httpCtxMock.VerifySet(
c => c.User = It.Is<IPrincipal>(
p => p.Identity.Name == DefaultUsername
&& p.Identity.IsAuthenticated));
}
[Test]
public void OnAuthorization_AuthenticationHeaderExists_SetPrincipalToCurrentThread()
{
var ctx = SetDefaultAuthData();
SetDefaultTicketToRequest(ctx);
_filter.OnAuthorization(ctx);
Thread.CurrentPrincipal.Identity.Name.Should().Be(DefaultUsername);
Thread.CurrentPrincipal.Identity.IsAuthenticated.Should().BeTrue();
}
private void AddDefaultAuthCookie(HttpActionContext ctx)
{
var cookieValue = string.Format("blah=blah_value;{0}={1}", FormsAuthentication.FormsCookieName, _defaultTicket);
ctx.Request.Headers.Add("Cookie", cookieValue);
}
private HttpActionContext SetDefaultAuthData()
{
SetupUserFromTicket(_defaultTicket, DefaultUsername);
var ctx = AuthActionContext();
return ctx;
}
private void SetDefaultTicketToRequest(HttpActionContext ctx)
{
ctx.Request.Headers.Add("Authentication", _defaultTicket);
}
private void SetupUserFromTicket(string ticket, string username)
{
_formsAuthMock.Setup(_ => _.Decrypt(ticket))
.Returns(new FormsAuthenticationTicket(username, false, 30));
}
private HttpActionContext AuthActionContext()
{
return NewActionContext("MethodWithAuth");
}
private HttpActionContext NewActionContext(string methodName)
{
return new HttpActionContext
{
ActionDescriptor = CreateActionDescriptor<TestController>(methodName),
ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() }
};
}
protected ReflectedHttpActionDescriptor CreateActionDescriptor<TApiController>(string actionName)
where TApiController : ApiController
{
var controllerType = typeof(TApiController);
return new ReflectedHttpActionDescriptor(
new HttpControllerDescriptor(
new HttpConfiguration(),
controllerType.Name.Split('.').Last(),
controllerType),
controllerType.GetMethod(actionName));
}
}
public class FormsAuthenticationFilter: IAutofacAuthorizationFilter
{
private readonly IFormsAuthWrapper _formsAuth;
private readonly HttpContextBase _httpContext;
public FormsAuthenticationFilter(IFormsAuthWrapper formsAuth, HttpContextBase httpContext)
{
_formsAuth = formsAuth;
_httpContext = httpContext;
}
public void OnAuthorization(HttpActionContext actionContext)
{
var anonymousAllowed = ControllerOrActionMarkedWith<AllowAnonymousAttribute>(actionContext);
if (anonymousAllowed)
{
return;
}
var request = actionContext.Request;
if (request == null)
{
SetUnathorizedResponse(actionContext);
return;
}
IEnumerable<string> authHeaderValues = null;
var authHeaderValue = request.Headers.TryGetValues("Authentication", out authHeaderValues)
? authHeaderValues.FirstOrDefault()
: GetTokenFromCookie(request);
if (string.IsNullOrEmpty(authHeaderValue))
{
SetUnathorizedResponse(actionContext);
return;
}
var ticket = _formsAuth.Decrypt(authHeaderValue);
if (ticket.Expired)
{
SetUnathorizedResponse(actionContext);
return;
}
var principal = new SimplePrincipal(ticket.Name);
_httpContext.User = principal;
Thread.CurrentPrincipal = principal;
}
private static string GetTokenFromCookie(HttpRequestMessage request)
{
var cookieHeaderValue = request.Headers.GetCookies(FormsAuthentication.FormsCookieName).FirstOrDefault();
if (cookieHeaderValue == null)
{
return null;
}
var targetCookie = cookieHeaderValue
.Cookies
.FirstOrDefault(cookie => cookie.Name == FormsAuthentication.FormsCookieName);
return targetCookie != null ? targetCookie.Value : null;
}
private static void SetUnathorizedResponse(HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.Unauthorized, "Authentication ticket was not specified or it is wrong.");
}
private static bool ControllerOrActionMarkedWith<TAttr>(HttpActionContext actionContext)
where TAttr : class
{
return ActionHasAttribute<TAttr>(actionContext)
|| ControllerHasAttribute<TAttr>(actionContext);
}
private static bool ControllerHasAttribute<TAttribute>(HttpActionContext actionCtx)
where TAttribute : class
{
var attributes = actionCtx
.ActionDescriptor
.ControllerDescriptor
.GetCustomAttributes<TAttribute>();
return attributes != null && attributes.Any();
}
private static bool ActionHasAttribute<TAttribute>(HttpActionContext actionCtx)
where TAttribute : class
{
var attributes = actionCtx
.ActionDescriptor
.GetCustomAttributes<TAttribute>();
return attributes != null && attributes.Any();
}
private class SimplePrincipal: IPrincipal
{
public SimplePrincipal(string userName)
{
Identity = new SimpleIdentity(userName);
}
public bool IsInRole(string role)
{
return true;
}
public IIdentity Identity { get; private set; }
}
private class SimpleIdentity: IIdentity
{
public SimpleIdentity(string name)
{
Name = name;
}
public string Name { get; set; }
public string AuthenticationType
{
get { return "Forms authentication thourgh HTTP headers."; }
}
public bool IsAuthenticated
{
get { return !string.IsNullOrEmpty(Name); }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment